设计模式(一):DDD领域驱动设计

  • DDD重架构,轻业务! – 从单体架构开始快速验证
  • DDD设计模式
    • 贫血模型
    • 充血模型
    • 防腐层
  • DDD四层架构规范
  • DDD单体架构和微服务架构隔离
    • 本地单体SPI 插拔式扩展业务模式
    • Nacos 插拔式扩展业务模式 ---- 服务名作为服务service code
  • DDD清晰架构 – 构建领域仓库,实现领域复用

领域驱动设计(DDD)在爱奇艺打赏业务的实践

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

DDD重架构,轻业务!— 从单体架构开始快速验证

在这里插入图片描述

DDD设计模式

1. 贫血模型

  • POJO 没有业务
  • 业务交由Service处理

2. 充血模型

  • DAO 负责和业务映射
  • DO 负责DB映射

3. 防腐层

  • 封控封装 BusiSafeService
  • Kafak封装 AduitMessageProducer
  • 金额计算 AccountTransferService
  • 用实体(Entity)封装单对象的状态行为
  • 领域服务封装多实体逻辑

变化前

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;
    }
}

变化后

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四层架构规范

  1. 领域层(Domain Layer): 系统的核心,纯粹表达业务能力,不需要任何外部依赖
  2. 应用层(Application Layer):协调领域对象,组织形成业务场景,只依赖于领域层
  3. 用户层(User Interface):负责与用户进行交互。理解用户请求,返回用户响应。只依赖于应用层
  4. 基础层(Infrastructure Layer):业务与数据分离。为领域层提供持久化服务,为其它层提供通用能力

在这里插入图片描述

adaptor 隔离各服务层
在这里插入图片描述

DDD单体架构和微服务架构隔离

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

本地单体SPI 插拔式扩展业务模式

@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 插拔式扩展业务模式 ---- 服务名作为服务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清晰架构 – 构建领域仓库,实现领域复用

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

在这里插入图片描述

体现形式
在这里插入图片描述
消息契约层: 在很多MVC项目的开发过程当中,会将传递数据的POJO,划分成DTO、VO、BO、DO等各种各样的Object,并且不断规定在每一个技术层面使用不同的Object,其目的也是为了尽量保证每一层的数据更纯洁,减少外部数据的影响

猜你喜欢

转载自blog.csdn.net/menxu_work/article/details/128565695