Welcome to pay attention to the public account “JAVA Front” to view more wonderful sharing articles, mainly including source code analysis, practical application, architecture thinking, workplace sharing, product thinking and so on, at the same time, welcome to add my wechat “JAVA_front” to communicate and learn together


1 DDD

Recently, I have been driving design in the learning field, and I have also learned COLA code and carried out some project practices. COLA code is neat and elegant, but there are certain learning costs and use costs. Finally, an engineering idea was to be implemented. I integrated some DDD technical frameworks, deleted CQRS and event bus mode, and sorted out a simple, practical and easy to implement project architecture.

(1) demo-infrastructure

Base layer. Include basic functions, such as database access, cache access, message sending, and a common toolkit

(2) demo-dependency

External access layer. In this module, the external RPC service is invoked to parse the return code and return data

(3) demo-domain

The domain layer. This module contains the BO (Business Object) similar to the three-tier architecture, but the difference is that it is defined using the congestion pattern, so the domain layer itself also contains the Business logic, rather than simply attribute declarations

(4) demo-service

The business layer. Although both the domain layer and the business layer contain businesses, they serve different purposes. Compared with the domain layer, the business layer can combine services of different domains and add flow control, monitoring, logging, and permission control facets

(5) demo-api

External interface layer. Provides externally oriented interface declarations

(6) demo-controller

External access layer. Provides externally oriented access points


2. Three-tier architecture and anaemic model

Before using the above framework, we generally used a three-tier architecture for business development:

Repository + Entity Service + BO (Business Object) Controller + VO (View Object)Copy the code

In three-tier architecture business development, anaemic model-based development is often used. An anaemic model is one in which the business logic is all in the Service layer and the business objects contain only the data but not the business logic. Let’s examine the code example.

/** * Account business object **@authorWechat official account "JAVA Front" * */
public class AccountBO {

    /** * Account ID */
    private String accountId;

    /** * Account balance */
    private Long balance;

    /** * Whether to freeze */
    private boolean isFrozen;

    public String getAccountId(a) {
        return accountId;
    }

    public void setAccountId(String accountId) {
        this.accountId = accountId;
    }

    public Long getBalance(a) {
        return balance;
    }

    public void setBalance(Long balance) {
        this.balance = balance;
    }

    public boolean isFrozen(a) {
        return isFrozen;
    }

    public void setFrozen(boolean isFrozen) {
        this.isFrozen = isFrozen; }}/** ** Transfer service implementation **@authorWechat official account "JAVA Front" * */
@Service
public class TransferServiceImpl implements TransferService {

    @Autowired
    private AccountService accountService;

    @Override
    public boolean transfer(String fromAccountId, String toAccountId, Long amount) {
        AccountBO fromAccount = accountService.getAccountById(fromAccountId);
        AccountBO toAccount = accountService.getAccountById(toAccountId);

        /** Check the transfer account **/
        if (fromAccount.isFrozen()) {
            throw new MyBizException(ErrorCodeBiz.ACCOUNT_FROZEN);
        }
        if (fromAccount.getBalance() < amount) {
            throw new MyBizException(ErrorCodeBiz.INSUFFICIENT_BALANCE);
        }
        fromAccount.setBalance(fromAccount.getBalance() - amount);

        /** Check the transfer account **/
        if (toAccount.isFrozen()) {
            throw new MyBizException(ErrorCodeBiz.ACCOUNT_FROZEN);
        }
        toAccount.setBalance(toAccount.getBalance() + amount);

        /** Update database **/
        accountService.updateAccount(fromAccount);
        accountService.updateAccount(toAccount);
        returnBoolean.TRUE; }}Copy the code

The TransferServiceImpl implementation class is anaemic model development, where AccountBO has only data and no business logic. The entire code style tends to be process-oriented, which is why some people call anemic models anti-patterns.


3. Hyperemia model

3.1 instance a

We introduced Domain layer in the congestion model based DDD development pattern. The Domain layer contains the business object BO, but not just the data. This layer also contains the business logic. Let’s examine the code example.

/** * Account business object **@authorWechat official account "JAVA Front" * */
public class AccountBO {

    /** * Account ID */
    private String accountId;

    /** * Account balance */
    private Long balance;

    /** * Whether to freeze */
    private boolean isFrozen;

    /**
     * 出借策略
     */
    private DebitPolicy debitPolicy;

    /** ** entry strategy */
    private CreditPolicy creditPolicy;

    /** * lending method **@paramThe amount amount * /
    public void debit(Long amount) {
        debitPolicy.preDebit(this, amount);
        this.balance -= amount;
        debitPolicy.afterDebit(this, amount);
    }

    /** * enter method **@paramThe amount amount * /
    public void credit(Long amount) {
        creditPolicy.preCredit(this, amount);
        this.balance += amount;
        creditPolicy.afterCredit(this, amount);
    }

    public boolean isFrozen(a) {
        return isFrozen;
    }

    public void setFrozen(boolean isFrozen) {
        this.isFrozen = isFrozen;
    }

    public String getAccountId(a) {
        return accountId;
    }

    public void setAccountId(String accountId) {
        this.accountId = accountId;
    }

    public Long getBalance(a) {
        return balance;
    }

    /** * BO and DO conversions must have a set method
    public void setBalance(Long balance) {
        this.balance = balance;
    }

    public DebitPolicy getDebitPolicy(a) {
        return debitPolicy;
    }

    public void setDebitPolicy(DebitPolicy debitPolicy) {
        this.debitPolicy = debitPolicy;
    }

    public CreditPolicy getCreditPolicy(a) {
        return creditPolicy;
    }

    public void setCreditPolicy(CreditPolicy creditPolicy) {
        this.creditPolicy = creditPolicy; }}/** * The entry policy implements **@authorWechat official account "JAVA Front" * */
@Service
public class CreditPolicyImpl implements CreditPolicy {

    @Override
    public void preCredit(AccountBO account, Long amount) {
        if (account.isFrozen()) {
            throw newMyBizException(ErrorCodeBiz.ACCOUNT_FROZEN); }}@Override
    public void afterCredit(AccountBO account, Long amount) {
        System.out.println("afterCredit"); }}/** * The lending policy implements **@authorWechat official account "JAVA Front" * */
@Service
public class DebitPolicyImpl implements DebitPolicy {

    @Override
    public void preDebit(AccountBO account, Long amount) {
        if (account.isFrozen()) {
            throw new MyBizException(ErrorCodeBiz.ACCOUNT_FROZEN);
        }
        if (account.getBalance() < amount) {
            throw newMyBizException(ErrorCodeBiz.INSUFFICIENT_BALANCE); }}@Override
    public void afterDebit(AccountBO account, Long amount) {
        System.out.println("afterDebit"); }}/** ** Transfer service implementation **@authorWechat official account "JAVA Front" * */
@Service
public class TransferServiceImpl implements TransferService {

    @Resource
    private AccountService accountService;
    @Resource
    private CreditPolicy creditPolicy;
    @Resource
    private DebitPolicy debitPolicy;

    @Override
    public boolean transfer(String fromAccountId, String toAccountId, Long amount) {
        AccountBO fromAccount = accountService.getAccountById(fromAccountId);
        AccountBO toAccount = accountService.getAccountById(toAccountId);
        fromAccount.setDebitPolicy(debitPolicy);
        toAccount.setCreditPolicy(creditPolicy);

        fromAccount.debit(amount);
        toAccount.credit(amount);
        accountService.updateAccount(fromAccount);
        accountService.updateAccount(toAccount);
        returnBoolean.TRUE; }}Copy the code

AccountBO contains a policy object that implements business logic. In this way, the business logic is implemented in the policy object, reducing the code to the process at the service level and making the code structure more cohesive.

3.2 instance 2

We also analyze an example of a cohesive congestion model that will verify logic.

/** * validator **@authorWechat official account "JAVA Front" * */
public interface BizValidator {
    BizValidateResult validate(a);
}

/** * Check result **@authorWechat official account "JAVA Front" * */
public class BizValidateResult {
    private boolean isSuccess;
    private String message;
    public BizValidateResult(boolean isSuccess, String message) {
        super(a);this.isSuccess = isSuccess;
        this.message = message;
    }
    public boolean isSuccess(a) {
        return isSuccess;
    }
    public void setSuccess(boolean isSuccess) {
        this.isSuccess = isSuccess;
    }
    public String getMessage(a) {
        return message;
    }
    public void setMessage(String message) {
        this.message = message; }}/** * Commodity business object **@authorWechat official account "JAVA Front" * */
public class GoodsBO implements BizValidator {
    private String goodsId;
    private String goodsName;
    private GoodsService goodsService;
    public String getGoodsId(a) {
        return goodsId;
    }
    public void setGoodsId(String goodsId) {
        this.goodsId = goodsId;
    }
    public String getGoodsName(a) {
        return goodsName;
    }
    public void setGoodsName(String goodsName) {
        this.goodsName = goodsName;
    }
    public GoodsService getGoodsService(a) {
        return goodsService;
    }
    public void setGoodsService(GoodsService goodsService) {
        this.goodsService = goodsService;
    }

    @Override
    public BizValidateResult validate(a) {
        if(StringUtils.isEmpty(goodsId)) {
            throw new MyBizException(ErrorCodeBiz.ILLEGAL_ARGUMENT);
        }
        if(StringUtils.isEmpty(goodsName)) {
            throw new MyBizException(ErrorCodeBiz.ILLEGAL_ARGUMENT);
        }
        Integer stock = goodsService.getGoodsStock(goodsId);
        if(stock <= 0) {
            throw new MyBizException(ErrorCodeBiz.NO_STOCK);
        }
        return new BizValidateResult(Boolean.TRUE, null); }}/** * order service implementation **@authorWechat official account "JAVA Front" * */
@Service
public class OrderServiceImpl implements OrderService {

    @Resource
    private GoodsService goodsService;

    @Override
    public String createOrder(GoodsBO goods) {
        if(null == goods) {
            throw new MyBizException(ErrorCodeBiz.ILLEGAL_ARGUMENT);
        }
        goods.setGoodsService(goodsService);
        goods.validate();
        System.out.println("Create order");
        return "orderId_111"; }}Copy the code


4 Some thoughts

One could argue that congestion just puts some business in the Domain layer, nothing special. I have the following reflections on this view:

(1) The code business style becomes more object-oriented rather than procedural, and the overall logic becomes more cohesive

(2) There is an open and closed principle in the design principle: open for expansion, closed for modification. I think this is the most important design principle. Many design patterns, such as strategy mode and template method mode, are designed based on this principle. The congestion model BO can contain various policies that can be managed using policy patterns

(3) Domain layer is not to replace Service layer, but a kind of supplementary enhancement, although Domain layer and business layer contain business but different purposes. Compared with the domain layer, the business layer can combine services of different domains and add flow control, monitoring, logging, and permission control facets

(4) The Lombok framework is very popular now, making code very concise. One caveat is that arbitrary use of Lombok can break code encapsulation, such as the fact that an AccountBO object should not expose the setBalance method. But it is also a tradeoff strategy because layer objects that require property copies must expose the setBalance method

Welcome to pay attention to the public account “JAVA Front” to view more wonderful sharing articles, mainly including source code analysis, practical application, architecture thinking, workplace sharing, product thinking and so on, at the same time, welcome to add my wechat “JAVA_front” to communicate and learn together