最后更新于2023年3月31日 20:15:17
问题建模=》软件分层=》具体结构,是层层递进的关系。有了问题建模,才能进行具体的软件分层的讨论,再有了分层,才能讨论在domain里面应该怎么实现具体结构。
1、问题建模:Domain、Model、Domain Model
领域驱动设计中的领域模型是什么? 这里面说的挺对的,领域模型是处理问题的条理化和结构化的知识,而领域本身就是问题/就是需要解决的业务。对甲方的需求的分析(甲方公司的需求、业务、规则、流程等)组成了domain。
先有了领域模型这个东西,再将领域模型转化为具体的代码,而代码的转化就要分什么用户界面/展现层、应用层、领域层、基础设施层,再往下才是具体的设计,什么实体、值对象之类的。
Domain and Core Domain in DDD 这是一个系列,看着也都不错。包括了限界上下文等内容。
限界上下文&通用语言
看起来它是对前一小节完成的领域建模的一个划分。
首先,领域被分为问题领域和解决方案领域,其中解决方案空间由一个或多个限界上下文组成。
在每个限界上下文中,只存在一种通用语言;
同一个词在不同的上下文,表示的意思也不同,比如“Developer”在软件工程(程序员)、房地产(开发商)、化学(显影剂)几个领域的含义不同。
4. 领域驱动设计——限界上下文 在这篇文章里面针对这部分只讲了很小的一点儿,也看不出限界上下文的重要性。
2、软件分层:User Interface、Application、Domain、Infrastructure关注点分离,构造四个概念层
DDD 架構: 分層式架構與依賴反向原則 这个博主讲话真的有够机车的喔!他这个也是稍稍深了一点。
DDD架构的重心是围绕Domain层进行构建的
6. Domain-Driven Design - Layered Architecture 这个挺好的,比较浅。这哥们儿在infrastructure那写的很烂,看得出来是懒得写下去了。写麻了是吧 点开他的个人资料,有一系列文章,应该都不错。
这一层可能是指前端,但是在一些没有前端的程序中,客户指的是外部的其它系统,于是也可以理解为暴露给外部使用的一些API。
这一层还有传统意义上的controller层的功能,即将传入的request转化为应用程序层的方法调用。
“负责为每个服务调用启动事务”是什么勾巴意思?!这里应该跟事务脚本模式有关。
几个栗子先,下面这是一个Application层的函数,从上面的文章里头扒的:
public void cancelOrder(String username, Long orderId) {// Check authorization on accountOrder order = brokerageAccountRepository.findOrder(orderId);checkAccountAuthorization(getUser(username),order.getAccount().getId(),BrokerageAccountPermission.Trade);// Cancel orderorder.pendingCancel(orderEventPublisher);exchangeTradingService.cancelOrder(order);
}
这个函数做的事情:输入username
和orderId
两个值对象,从brokerageAccountRepository
这个infrastructure层的对象里identify到唯一的order
Domain=》检查账户的认证状态(这个我也不好说,调用了很多东西,很乱……)=》调用order
Domain来取消订单=》从exchangeTradingService
里面取消订单,这个不讨论。
根据上面这个函数,总结两点:1、该应用层的函数通过一个值对象,到infra层去检索一个聚合
。2、该应用层的函数还能操作这个检索到的聚合
,触发一些domain层的事情,就像上面提到的 order.pendingCancel(orderEventPublisher)
这是DDD的重中之重,这个Domain跟建模时候那个Domain是两码事,这个放在第三小节展开了讲。
3、Domain层
Domain层是整个软件设计的核心部分,展开单独分配一小节来讨论。
参考文章:7. Domain-Driven Design - Domain Layer
这句话说的挺好的:
This layer is the heart of the application. It consists of entities, value objects, domain services and domain events.
这个写的好:Aggregates & Entities in Domain-Driven Design
举个例子:一个订单purchase order(PO)可能包含多个订单项Line items. 比如说结个婚要包含五金、彩礼、车子、房子啥的,一个道理。 聚合支持更高程度的概念。line items可以单独用,但是没啥意义。而且line items可能粒度太小,太麻烦。
这个我觉得说的挺好,一个PO是由entity和好多value object组成的一个聚合aggregate。这里面,PO这个entity是这个聚合的根。
业务逻辑被绑定在值对象上,而非实体上,实体更多地用来做identify。
一个聚合可能包含多个实体,比如说一个账户聚合可能包含一个账户实体和多个交易实体,通过定义事务边界来涵盖一个客户的存款/取款动作。
identify
是啥意思:ChatGPT查了一下,差不多就是说不管实体中的值对象怎么变化,实体的身份不会变。就好像一个客户,包含电子邮箱、资产啥的都会变,但是客户就是客户,不会变成摩托车或者油轮什么的。
我上面划掉的这句话的理解不太对,identify就像一本书的isbn号似的,唯一确定一个实体。
GoogleGroup的一个讨论
实体:域内具有重要意义(例如客户)并且可以随时间变化的唯一对象
值对象:域内的不可变对象,在其属性(例如日期、地址)之外没有任何意义
聚合:通过根对象相互关联的实体或值对象的集合
聚合根:“拥有”聚合并充当聚合内所有修改的网关的实体
DDD 戰術設計:Domain Service 这篇文章里有讲到domain service和application service的区别。这篇文章写的比较接地气,但是我觉得它是针对的对这两个概念有一定了解的同学,所以我没看太懂。
如何分辨领域服务与应用服务?
DDD理论学习系列(8)-- 应用服务&领域服务 应用服务属于application层
Practical DDD in Golang: Domain Service这一套文章都很好,收费的
chatGPT的回答供参考:
In Domain-Driven Design (DDD), whether to use a domain object or a domain service to process business logic depends on the nature of the logic itself.
Domain objects should be used to encapsulate behavior that is intrinsic to the object itself. In other words, behavior that is closely related to the object’s state and behavior that should be maintained within the object. For example, a Customer object might have a behavior like “placeOrder” that encapsulates the process of placing an order, and updates the state of the object accordingly.
On the other hand, domain services should be used to encapsulate behavior that doesn’t naturally belong to any particular domain object. These services often have broader responsibilities and may involve multiple domain objects in their operation. For example, a PaymentService might handle the payment process for an order, involving multiple domain objects such as Order, Payment, and Customer.
In summary, use a domain object when the behavior is intrinsic to the object itself and use a domain service when the behavior is not closely related to any particular domain object or involves multiple domain objects.
同样,在上面那个DDD理论学习(8)里面也提到了:
当领域中的某个操作过程或转换过程不是实体或值对象的职责时,我们便应该将该操作放在一个单独的接口中,即领域服务。请确保该服务和通用语言时一致的;并且保证它是无状态的。
这取决于业务逻辑与领域对象的相关性,例如一个客户对象有一个“下订单”的行为,这个行为封装了下订单的处理逻辑+更新了订单状态,则放在领域对象中;一个支付服务可能处理一个订单的支付过程,这里面涉及到了多个领域对象,则放在领域服务中。
DDD理论学习系列(9)-- 领域事件 这个系列讲的真是好。
Domain events: Design and implementation 下面有很多讲领域事件的链接。
实现领域事件 我感觉领域事件是DDD跟ES的一个耦合的点,在做技术分享的时候可以很好地从DDD过渡到ES。这个链接里的例子蛮好的,截个图:
它这个实现了一个简单的注册+登录的demo(spring-boot实现),可以试着改成Golang。
上面这个我有点理解了,就是说saveReply和sendPush可以拆分到不同的领域中,作为不同的listener存在。此时reply这个service就只负责发送一个reply事件到各个listener即可。这些saveReply,sendPush啥的就是领域事件。
What’s the Difference Between an Aggregate and a Bounded Context 这个文章太深了。。。
什么是域逻辑? 这个我也没看明白。。。
4、适用于DDD的设计模式等技术
DDD 中的那些模式 — CQRS 我截了一张图放下面了,这个图挺好的。这个就先不放在技术分享里面说了。
Event Sourcing Example & Explained in plain English 源码收费,想给他个大逼斗,给我死。
Why Event Sourcing? 这个是有关于一个叫eventuate local的微服务开源框架的,这一篇有点儿用,剩下都是废话;
记录事件,用replay事件的方式去重组实体entity。作为消息的载体,允许services去订阅这些事件。事件存储是事件驱动结构的支柱。
Event Sourcing 这个太早了。05年的,不一定有用。
将应用程序的状态的所有更改都存储为事件序列……前面都是老调重弹;
Structuring the Event Handler Logic(构建事件处理的逻辑)这个没人提到过:事务脚本/域模型是什么勾巴东西?!
事件逆转:把+10美元变成-10美元,感觉这个水ppt挺有用的……
…………………………还有一些奇怪的点,都看完估计我就困了,主要看示例吧。
Event Sourcing in Go 写的不好,都是代码片段,不看了。
5、ES相关的开源package
Benthos
其它
DDD与es之间的联系;
事件溯源与事件驱动之间的区别和关系:
写一个基于我们的新系统的例子;