热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

…接上六.实践:战术设计​    从某种意义上说,战略设计代表了计划能力,而战术设计代表了执行力。本节我们就来执行一下,因为本领域模型虽然并不复杂,但是如果把所有模型都贴出来也不太



…接上


六. 实践:战术设计

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


用户管理上下文

在这里插入图片描述
​ 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 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 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 :
在这里插入图片描述
​     把实体(Entity)和值对象(Value Object)在一致性边界之内组成聚合(Aggregate)乍看起来是一项比较简单和轻松的任务,但在DDD的众多战术指导中却是最难理解的。一个需要明确回答的关键问题是:聚合的不变条件和一致性边界究竟是什么?笔者本人也还没有这个水平来正确回答这个问题,个人的理解是聚合本身应该保证业务规则的不变性和一致性。

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

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

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


订单与支付上下文

在这里插入图片描述
​ 这个上下文由两个聚合来组成,分别是订单和支付。

CML 代码:

BoundedContext orderContext implements businessDomain {
type = FEATURE
domainVisiOnStatement= "Orders Management"
implementatiOnTechnology= "Java, SpringBoot"
knowledgeLevel = CONCRETE
Aggregate Order {
Entity Order {
aggregateRoot

- OrderID orderId
- List 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 :

在这里插入图片描述
在这里插入图片描述
​     在实体的构建中,我们应该明确实体的本质特征,挖掘其关键行为,定义其角色和职责,并使其可验证、可跟踪。很多时候,以更为轻量级的不可修改的值对象来代替实体是一个不错的选择。

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

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

未完,待续…



推荐阅读
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文介绍了一个适用于PHP应用快速接入TRX和TRC20数字资产的开发包,该开发包支持使用自有Tron区块链节点的应用场景,也支持基于Tron官方公共API服务的轻量级部署场景。提供的功能包括生成地址、验证地址、查询余额、交易转账、查询最新区块和查询交易信息等。详细信息可参考tron-php的Github地址:https://github.com/Fenguoz/tron-php。 ... [详细]
  • NotSupportedException无法将类型“System.DateTime”强制转换为类型“System.Object”
    本文介绍了在使用LINQ to Entities时出现的NotSupportedException异常,该异常是由于无法将类型“System.DateTime”强制转换为类型“System.Object”所导致的。同时还介绍了相关的错误信息和解决方法。 ... [详细]
  • 全面介绍Windows内存管理机制及C++内存分配实例(四):内存映射文件
    本文旨在全面介绍Windows内存管理机制及C++内存分配实例中的内存映射文件。通过对内存映射文件的使用场合和与虚拟内存的区别进行解析,帮助读者更好地理解操作系统的内存管理机制。同时,本文还提供了相关章节的链接,方便读者深入学习Windows内存管理及C++内存分配实例的其他内容。 ... [详细]
author-avatar
adfa3sd5f6a
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有