DDD(Domain Driven Design) 领域驱动设计从理论到实践 八

…接上

六. 实践:战术设计

​     从某种意义上说,战略设计代表了计划能力,而战术设计代表了执行力。本节我们就来执行一下,因为本领域模型虽然并不复杂,但是如果把所有模型都贴出来也不太现实。笔者这里展示两个界限上下文的设计,只是个初稿,有很多不足,非常希望都到广大读者和专业人士的指正。

用户管理上下文

DDD(Domain Driven Design) 领域驱动设计从理论到实践 八
​ CML 代码:

BoundedContext userManagementContext implements userDomain {
    type = FEATURE
    domainVisionStatement = "User management from system view"
    implementationTechnology = "Java, SpringBoot"
    responsibilities = "User", "Role"
    knowledgeLevel = CONCRETE

    Aggregate User {
        responsibilities = "User"
        knowledgeLevel = CONCRETE
        securityZone "Internal"
        contentVolatility = NORMAL
        consistencyCriticality = HIGH
        securityCriticality = HIGH

        Entity User {
            aggregateRoot

            - UserID userId
            - Status userStatus
            - UserType userType
            - BasicProfile profile
            - List<Role> roles
            - UserQualify qualify
            - Account account
            DateTime registeredTime
            DateTime updatedTime

            def int UpdateUserProfile(userIdentification id) : write;
            def int CreateUser() : write [ ->CREATED ];
            def int DropUser() : write [ ->REMOVED ];
            def int ActivatedUser() : write [ ->ACTIVATED ];
            def int DeactivatedUSer() : write [ ACTIVATED -> DEACTIVATED ];
            def int UpdateUserRole();
            def User ReadUserProfile() : read-only;

            Repository UserRepository {
                @User get(@UserID id);
                List<@User> getAll();
            }
        }

        ValueObject UserID {
            long userId
        }

        ValueObject BasicProfile {
            String userName
            String password
            String cellPhone
            DateTime birthDate
            String mailAddress
            String alternativeMail
        }

        ValueObject Account {
            - UserID userId
            - List<VirtualCurrency> virtualCurr
        }

        abstract ValueObject VirtualCurrency {
            float amount
        }

        ValueObject Money extends VirtualCurrency {
            - Currency curr
            float mapping
            baseCurrency rmb
        }

        ValueObject Score extends VirtualCurrency {
            float ratio
            baseCurrency rmb
        }

        enum Currency {
            RMB,DOLLAR
        }

        enum UserType {
            USER, ROLEADMIN, DOMAINADMIN, SITEADMIN
        }

        enum Status {
            aggregateLifecycle
            ACTIVATED, DEACTIVATED, CREATED, REMOVED
        }

        abstract DomainEvent AbstractDomainEvent {
            DateTime timestamp
        }

        DomainEvent UserProfileChanged extends AbstractDomainEvent {}
        DomainEvent UserCreated extends AbstractDomainEvent {}
        DomainEvent UserRemoved extends AbstractDomainEvent {}
        DomainEvent UserActivated extends AbstractDomainEvent {}
        DomainEvent UserDeactivated extends AbstractDomainEvent {}
        DomainEvent UserRolesAsscioationChanged extends AbstractDomainEvent {}

    }

    Aggregate Role {
        responsibilities = "Role"
        knowledgeLevel = CONCRETE

        ValueObject Role {
            aggregateRoot

            - RoleType roleType  
            String desc
            - Status status
            - Rules defaultRule

            def int CreateRole();
            def int CreatedCustomedRole();
            def int UpdateRole();
            def int ActivatedRole();
            def int DeactivatedRole();
            def int DropRole();

        }

        enum RoleType {
            ORG, PARTNER, CHANNEL, MEMBER, CUSTOMED
        }

        DomainEvent RoleCreated extends AbstractDomainEvent {}
        DomainEvent RoleRemoved extends AbstractDomainEvent {}
        DomainEvent RoleActivated extends AbstractDomainEvent {}
        DomainEvent RoleDeactivated extends AbstractDomainEvent {}
        DomainEvent RoleProfileChanged extends AbstractDomainEvent {}

    }

    Aggregate UserQualify {
        responsibilities = "Role"
        knowledgeLevel = CONCRETE

        ValueObject UserQualify {
            aggregateRoot

            - Qualify qualify
            String desc
        }

        enum Qualify {
            DOCTOR,RESEARCHER,LIBRARIAN,OTHERS
        }

    }

}

UML :
DDD(Domain Driven Design) 领域驱动设计从理论到实践 八
​     把实体(Entity)和值对象(Value Object)在一致性边界之内组成聚合(Aggregate)乍看起来是一项比较简单和轻松的任务,但在DDD的众多战术指导中却是最难理解的。一个需要明确回答的关键问题是:聚合的不变条件和一致性边界究竟是什么?笔者本人也还没有这个水平来正确回答这个问题,个人的理解是聚合本身应该保证业务规则的不变性和一致性。

​     DDD本身主张小的聚合,因为一个聚合如果引入了太多对象时,整个对象的加载和更新操作将会变得很沉重。例如大的聚合在维护整体事务一致性上将会面临麻烦,从而限制了系统的性能和可伸缩性。

​     DDD推荐聚合的实现时遵循迪米特法则告诉非询问原则。前者强调最小知识,后者更为简单。

​     在上面所展示的用户管理上下文的实现中,这个上下文是由两个聚合组成的,分别是用户和角色。

订单与支付上下文

DDD(Domain Driven Design) 领域驱动设计从理论到实践 八
​ 这个上下文由两个聚合来组成,分别是订单和支付。

CML 代码:

BoundedContext orderContext implements businessDomain {
    type = FEATURE
    domainVisionStatement = "Orders Management"
    implementationTechnology = "Java, SpringBoot"
    knowledgeLevel = CONCRETE

    Aggregate Order {
        Entity Order {
            aggregateRoot
			
            - OrderID orderId
            - List<OrderItem> items
            - OrderState orderState
            - @Policies policies
            long userId
            DateTime createTime
            DateTime completeTime

            def calculateSumPrice();
            def postOrderAction();
        }

        enum OrderState {
            aggregateLifecycle
            PAYED,UNPAYED,CANCELED
        }

        ValueObject OrderID {
            int id
        }
       
        ValueObject OrderItem {
            int productId
            float price
        }
        
        DomainEvent OrderSubmitted {}
        DomainEvent OrderRevokedSucc {}
        DomainEvent OrderRevokedFail {}
        DomainEvent OrderPostActionFinished {}
        
    }
}

BoundedContext payContext implements businessDomain {
    type = FEATURE
    domainVisionStatement = "Pay Management"
    implementationTechnology = "Java, SpringBoot"
    knowledgeLevel = CONCRETE

    Aggregate Payment {

        Service PaymentService {
            int doPayment(int orderId) throws PaymentFailedException;
            int rollback(int paymentId) throws paymentRollbackFailedException;
        }

        ValueObject PaymentID {
            int paymentId
        }

        enum PayMethod {
            WEIXIN,ZHIFUBAO,CREDITCARD,SCORE
        }

        DomainEvent PaymentSucceed {}
        DomainEvent PaymentFailed {}
        DomainEvent PaymentRollbacked {}

    }
    
}

UML :

DDD(Domain Driven Design) 领域驱动设计从理论到实践 八
DDD(Domain Driven Design) 领域驱动设计从理论到实践 八
​     在实体的构建中,我们应该明确实体的本质特征,挖掘其关键行为,定义其角色和职责,并使其可验证、可跟踪。很多时候,以更为轻量级的不可修改的值对象来代替实体是一个不错的选择。

​     本节以图和CML代码的形式分享了DDD战术设计实践,这中间有太多不够完善的地方。读者可以仔细阅读CML代码来了解具体细节,CML语法可参阅前面所提到的 context mapper 。再次强调示例中只是一个非常粗陋的初版,存在非常多的不足和缺陷,距离一个完整和全面的DDD战术设计还很远。例如如何解决N:N关系,持续集成、接口幂等性等等都没有提及,但这些也是设计过程中必须要考虑的。

​     读者在具体的实现时需要谨慎斟酌,结合DDD设计理念和面向对象分析技术反复迭代才能够取得好的效果,从而实现把核心业务逻辑和业务处理能力沉淀到平台层。

未完,待续…

上一篇:httprunner3源码解读(3)client.py


下一篇:初次尝试GPU Driven —— 大范围植被渲染