• 【DDD】编码实战


    参考:https://insights.thoughtworks.cn/backend-development-ddd/  后端开发实践系列——领域驱动设计(DDD)编码实践


    战略设计更偏向于软件架构,那么战术设计便更偏向于编码实现。DDD战术设计的目的是使得业务能够从技术中分离并突显出来,让代码直接表达业务的本身,其中包含了聚合根、应用服务、资源库、工厂等概念。

    源码参考: https://github.com/e-commerce-sample/ecommerce-order-service


    实现业务的3种方式对比

    1、service+贫血模型存在一个贫血的“领域对象”,业务逻辑通过一个Service类实现,然后通过setter方法更新领域对象,最后通过DAO(多数情况下可能使用诸如Hibernate之类的ORM框架)保存到数据库中。

         职责划分模糊不清,使本应该内聚在Order中的业务逻辑泄露到了其他地方(OrderService),导致Order成为一个只是充当数据容器的贫血模型(Anemic Model),而非真正意义上的领域模型。在项目持续演进的过程中,这些业务逻辑会分散在不同的Service类中,最终的结果是代码变得越来越难以理解进而逐渐丧失扩展能力。

    @Transactional
    public void changeProductCount(String id, ChangeProductCountCommand command) {
        Order order = DAO.findById(id);
        if (order.getStatus() == PAID) {
            throw new OrderCannotBeModifiedException(id);
        }
        OrderItem orderItem = order.getOrderItem(command.getProductId());
        orderItem.setCount(command.getCount());
        order.setTotalPrice(calculateTotalPrice(order));
        DAO.saveOrUpdate(order);
    }

    2、事务脚本在不使用ORM的情况下,领域对象甚至都没有必要存在。于是,此时的代码实现便退化成了事务脚本(Transaction Script),也就是直接将Service类中计算出的结果直接保存到数据库(或者有时都没有Service类,直接通过SQL实现业务逻辑):

    @Transactional
    public void changeProductCount(String id, ChangeProductCountCommand command) {
        OrderStatus orderStatus = DAO.getOrderStatus(id);
        if (orderStatus == PAID) {
            throw new OrderCannotBeModifiedException(id);
        }
        DAO.updateProductCount(id, command.getProductId(), command.getCount());
        DAO.updateTotalPrice(id);
    }

    3、DDD核心的业务逻辑被内聚在行为饱满的领域对象

    class Order {
        public void changeProductCount(ProductId productId, int count) {
            if (this.status == PAID) {
                throw new OrderCannotBeModifiedException(this.id);
            }
            OrderItem orderItem = retrieveItem(productId);
            orderItem.updateCount(count);
        }
    }
    
    // Controller
    @PostMapping("/order/{id}/products")
    public void changeProductCount(@PathVariable(name = "id") String id, @RequestBody @Valid ChangeProductCountCommand command) {
        Order order = DAO.byId(orderId(id));
        order.changeProductCount(ProductId.productId(command.getProductId()), command.getCount());
        order.updateTotalPrice();
        DAO.saveOrUpdate(order);
    }

     目录划分

    基于聚合根进行顶层包的划分:“内聚性”原则的典型代表,在各自的顶层包下再根据代码结构的复杂程度划分子包

    如果子包下只有单个文件,足够简单,可以不创建子包

    ├── order
    │   ├── OrderApplicationService.java
    │   ├── OrderController.java
    │   ├── OrderPaymentProxy.java
    │   ├── OrderPaymentService.java
    │   ├── OrderRepository.java
    │   ├── command
    │   │   ├── ChangeAddressDetailCommand.java
    │   │   ├── CreateOrderCommand.java
    │   │   ├── OrderItemCommand.java
    │   │   ├── PayOrderCommand.java
    │   │   └── UpdateProductCountCommand.java
    │   ├── exception
    │   │   ├── OrderCannotBeModifiedException.java
    │   │   ├── OrderNotFoundException.java
    │   │   ├── PaidPriceNotSameWithOrderPriceException.java
    │   │   └── ProductNotInOrderException.java
    │   ├── model
    │   │   ├── Order.java
    │   │   ├── OrderFactory.java
    │   │   ├── OrderId.java
    │   │   ├── OrderIdGenerator.java
    │   │   ├── OrderItem.java
    │   │   └── OrderStatus.java
    │   └── representation
    │       ├── OrderItemRepresentation.java
    │       ├── OrderRepresentation.java
    │       └── OrderRepresentationService.java

    除上述之外,可创建单独的common类(下含util/log/xxx)等

  • 相关阅读:
    LeetCode 100. 相同的树(Same Tree) 2
    LeetCode 680. 验证回文字符串 Ⅱ(Valid Palindrome II) 1
    MySQL索引操作
    MySQL数据库的一些方法使用
    Anaconda安装新模块
    源码下载
    mongodb内建角色
    windows server 2008开启共享文件设置
    MySQL配置说明
    MySQL的连接数
  • 原文地址:https://www.cnblogs.com/clarino/p/15510589.html
Copyright © 2020-2023  润新知