Design pattern (1): DDD domain-driven design

  • DDD focuses on architecture and light on business! – Quick verification from the monolithic architecture
  • DDD design pattern
    • Anemia model
    • hyperemia model
    • Anti-corrosion layer
  • DDD four-layer architecture specification
  • DDD monolithic architecture and microservice architecture isolation
    • Local single SPI pluggable expansion business model
    • Nacos pluggable extension business model - service name as service code
  • DDD clear architecture – build a domain warehouse to realize domain reuse

Practice of Domain-Driven Design (DDD) in iQiyi’s tipping business

https://blog.csdn.net/weixin_45583158/article/details/114312926

DDD focuses on architecture and light on business! — Quick verification from the monolithic architecture

insert image description here

DDD design pattern

1. Anemia Model

  • POJO has no business
  • The business is handed over to Service

2. Hyperemia model

  • DAO responsible and business mapping
  • DO is responsible for DB mapping

3. Anti-corrosion layer

  • Encapsulation and encapsulation of BusiSafeService
  • Kafak encapsulates AduitMessageProducer
  • Amount CalculationAccountTransferService
  • Encapsulate the state behavior of a single object with an entity (Entity)
  • Domain services encapsulate multi-entity logic

before change

public class PaymentController{
    
    
    private PayService payService;
    public Result pay(String merchantAccount,BigDecimal amount){
    
    
        Long userId = (Long) session.getAttribute("userId");
  return payService.pay(userId, merchantAccount, amount);
    }
}

public class PayServiceImpl extends PayService{
    
    
    private AccountDao accountDao;//操作数据库
    private KafkaTemplate<String, String> kafkaTemplate;//操作kafka
    private RiskCheckService riskCheckService;//风控微服务接口
    
    public Result pay(Long userId,String merchantAccount,BigDecimal amount){
    
    
        // 1. 从数据库读取数据
        AccountDO clientDO = accountDAO.selectByUserId(userId);
        AccountDO merchantDO =
        accountDAO.selectByAccountNumber(merchantAccount);
        // 2. 业务参数校验
        if (amount>(clientDO.getAvailable()) {
    
    
         throw new NoMoneyException();
        }
        // 3. 调用风控微服务
        RiskCode riskCode = riskCheckService.checkPayment(...);
        // 4. 检查交易合法性
        if("0000"!= riskCode){
    
    
            throw new InvalideOperException();
        }
        // 5. 计算新值,并且更新字段
        BigDecimal newSource = clientDO.getAvailable().subtract(amount);
        BigDecimal newTarget = merchantDO.getAvailable().add(amount);
        clientDO.setAvailable(newSource);
        merchantDO.setAvailable(newTarget);
        // 6. 更新到数据库
        accountDAO.update(clientDO);
        accountDAO.update(merchantDO);
        // 7. 发送审计消息
        String message = sourceUserId + "," + targetAccountNumber + "," + targetAmount;
        kafkaTemplate.send(TOPIC_AUDIT_LOG, message);
        return Result.SUCCESS;
    }
}

after change

public class PaymentController{
    
    
    private PayService payService;
    public Result pay(String merchantAccount,BigDecimal amount){
    
    
        Long userId = (Long) session.getAttribute("userId");
  return payService.pay(userId, merchantAccount, amount);
    }
}

public class PayServiceImpl extends PayService{
    
    
    private AccountRepository accountRepository;
    private AuditMessageProducer auditMessageProducer;
    private BusiSafeService busiSafeService;
    private AccountTransferService accountTransferService;
    
    public Result pay(Long userId,String merchantAccount,BigDecimal amount){
    
    
        // 参数校验
        Money money = new Money(amount);
        UserId clientId = new UserId(userId);
        AccountNumber merchantNumber = new AccountNumber(merchantAccount);
        // 读数据
        Account clientAccount = accountRepository.find(clientId);
        Account merAccount = accountRepository.find(merchantNumber);
        // 交易检查
        Result preCheck =  busiSafeService.checkBusi(clientAccount,merAccount,money);
        if(preCheck != Result.SUCCESS)return Result.REJECT;// 业务逻辑
  accountTransferService.transfer(clientAccount,merAccount,money);
        // 保存数据
        accountRepository.save(clientAccount);
        accountRepository.save(merAccount);
        // 发送审计消息
        AuditMessage message = new AuditMessage(clientAccount, merAccount,money);
  auditMessageProducer.send(message);
        return Result.SUCCESS;
    }
}

DDD four-layer architecture specification

  1. Domain Layer: The core of the system, purely expressing business capabilities without any external dependencies
  2. Application Layer: coordinates domain objects, organizes and forms business scenarios, and only depends on the domain layer
  3. User Interface (User Interface): Responsible for interacting with users. Understand user requests and return user responses. Only depends on the application layer
  4. Infrastructure Layer: Separation of business and data. Provide persistence services for the domain layer and general capabilities for other layers

insert image description here

The adapter isolates each service layer
insert image description here

DDD monolithic architecture and microservice architecture isolation

//TODO 单体架构与微服务架构只需要切换一个服务实现类即可,对各领域的核心是没有影响的。。
//@Resource(name = "LocalAsyncBusiService") //基于SPI实现的单体架构消息处理服务
@Resource(name = "NacosAsyncBusiService") //基于Nacos实现的微服务消息处理服务
private AsyncBusiService asyncBusiService;

Local single SPI pluggable expansion business model

@Component
public class BusiServiceRepositoryimpl implements BusiServiceRepository {
    
    

    private List<GsService> busiServices = new ArrayList<>();

    @PostConstruct
    public void loadBusiServices() {
    
    
        if (busiServices.isEmpty()) {
    
    
            final ServiceLoader<GsService> gsServices = ServiceLoader.load(GsService.class);
            final Iterator<GsService> iterator = gsServices.iterator();
            while (iterator.hasNext()) {
    
    
                busiServices.add(iterator.next());
            }
        }
    }

    public GsService getGsService(String serviceCode) {
    
    
        for (GsService gsService : busiServices) {
    
    
            if (serviceCode.equals(gsService.serviceCode())) {
    
    
                return gsService;
            }
        }
        return null;
    }
}

Nacos pluggable extension business model - service name as service code

@Resource
private DiscoveryClient discoveryClient;

//1、去Nacos上获取服务实例。
List<ServiceInstance> instances = discoveryClient.getInstances(serviceCode);
//1-1、如果服务不存在,直接返回不支持的serviceCode
if(null == instances || instances.isEmpty()){
    
    
    logger.error("ServiceCode["+serviceCode+"] is not support yet.");
    return;
}
StringBuffer requestUrl = new StringBuffer("http://");
ServiceInstance serviceInstance;
//随机找一个实例
if(instances.size()==1){
    
    
    serviceInstance = instances.get(0);
}else{
    
    
    Random random = new Random();
    int index = random.nextInt(instances.size());
    serviceInstance = instances.get(index);
}
//接口路径固定请求 SERVICE_SUFFIX
requestUrl.append(serviceInstance.getHost()).append(":").append(serviceInstance.getPort()).append(SERVICE_SUFFIX);

DDD clear architecture – build a domain warehouse to realize domain reuse

https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together/

insert image description here

Embodied form
insert image description here
message contract layer: In the development process of many MVC projects, the POJO that transmits data will be divided into various Objects such as DTO, VO, BO, DO, etc., and it is constantly stipulated that different methods are used at each technical level. Object, its purpose is to try to ensure that the data of each layer is more pure and reduce the influence of external data

Guess you like

Origin blog.csdn.net/menxu_work/article/details/128565695