惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
V
Visual Studio Blog
P
Privacy International News Feed
月光博客
月光博客
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
L
Lohrmann on Cybersecurity
N
News and Events Feed by Topic
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Apple Machine Learning Research
Apple Machine Learning Research
阮一峰的网络日志
阮一峰的网络日志
Webroot Blog
Webroot Blog
T
Threatpost
宝玉的分享
宝玉的分享
The Last Watchdog
The Last Watchdog
小众软件
小众软件
L
LINUX DO - 最新话题
C
Cisco Blogs
T
Troy Hunt's Blog
Schneier on Security
Schneier on Security
酷 壳 – CoolShell
酷 壳 – CoolShell
www.infosecurity-magazine.com
www.infosecurity-magazine.com
雷峰网
雷峰网
G
GRAHAM CLULEY
有赞技术团队
有赞技术团队
Know Your Adversary
Know Your Adversary
博客园 - 叶小钗
罗磊的独立博客
V
V2EX
博客园 - Franky
P
Proofpoint News Feed
SecWiki News
SecWiki News
腾讯CDC
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Jina AI
Jina AI
博客园 - 三生石上(FineUI控件)
S
Secure Thoughts
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Google DeepMind News
Google DeepMind News
Attack and Defense Labs
Attack and Defense Labs
人人都是产品经理
人人都是产品经理
The Cloudflare Blog
PCI Perspectives
PCI Perspectives
V2EX - 技术
V2EX - 技术
Google DeepMind News
Google DeepMind News
Last Week in AI
Last Week in AI
aimingoo的专栏
aimingoo的专栏
Cisco Talos Blog
Cisco Talos Blog
N
News and Events Feed by Topic
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
S
SegmentFault 最新的问题

博客园 - 小水坝

搞定thrift双向消息 读《软件需求最佳实践》有感 【thrift】vc中使用thrift中文字符串乱码问题解决 __declspec(dllimport)的小秘密(转) 跨线程send message 【boost】使用serialization库序列化子类 【boost】ptree 读写中文的问题 动态创建TeeChart的简便方法 【MFC】动态创建CMFCToolbar图标不显示问题 【boost】使用装饰者模式改造boost::thread_group 【VC】VC工具栏图标合并工具(非tbcreator和visual toolbar) 【boost】使用lambda表达式和generate_n生成顺序序列 【boost】BOOST_LOCAL_FUNCTION体验 【boost】MFC dll中使用boost thread的问题 【转帖】C++编译原理 资料 IE6,7下password框长度变短问题 dwz局部表格分页 dwz中combox的value问题 dwz中使用flot,js报表等js插件
使用双分派解决领域实体和外部机制通信问题
小水坝 · 2015-09-17 · via 博客园 - 小水坝

使用双分派解决领域实体和外部机制通信问题

经典DDD分层中领域层领域实体自身高内聚,在领域层内通过聚合,实体,事件和仓储接口完成领域业务逻辑。
最近在实际项目中遇到一种场景,有如下所示实体Device:

public class Device
{
	void ExecuteCmd(string cmd)
	{
		//Do something "Domian" related before sending..
		//Send command to real device via network
	}
}

其中ExecuteCmd方法需要首先将指令通过网络发送到实际设备。由于领域实体自身不能完成诸如Email,网络操作等实际业务功能,故该功能只能通过某种服务完成。但是执行这个动作和其结果,流程却属于领域部分,故不能全部交由应用层来实现,否则将导致部分业务逻辑从领域层泄露。
为了解决这个问题,先后尝试了三种方式:

  • 使用ServiceLocate模式向领域实体中注入服务接口
  • 使用领域事件,在事件处理器重注入服务接口
  • 使用双分派模式,传入执行接口

1实体中注入服务接口

public Interface IDeviceService
{
	void Execute(string cmd);
}
public class Device
{
	private IDeviceAgent _agent = ServiceLocator.GetInstance<IDeviceService>();
	void ExecuteCmd(string cmd)
	{
		//Do something "Domian" related before sending..
		//invoke agent
		_agent.Execute(cmd);
	}
}

该模式最简单,但却是DDD中的反模式,造成的后果是使得实体不再“纯净”,需要依赖一个服务接口,这样将给单元测试带来麻烦,并且容忍了这种方式之后,极有可能造成后续将Repository接口等注入实体,使得领域实体进一步腐化。

2使用领域事件

该方式下在需要定义领域事件,而在事件处理器中注入服务接口。此方法符合DDD要求,但在经典DDD分层项目中显得比较繁琐,需要频繁定义细粒度事件及事件处理器。并且调用依赖领域服务总线实现,总线消息实现为延迟传递则会带来一定问题。同样的,在这种方式下,单元测试也有一定的麻烦。

3使用双分派模式传入领域服务接口

Chassaging在这篇文章

If I have mail message entity and I want a Send method on it ?
Tiding your entity to the service that will use it is not conceptually satisfying. The
server >that will send the message is a medium and is not part of the entity itself.
It’s seems better to call server.Send(message).
The problem is that you can end breaking the tell don’t ask principle because the Send
method will ask the message for each property value. And you can be tempted to put
computations that should be in the message entity inside the Send method.

Let’s call Double Dispatch to the rescue !

  • Design a server interface presenting a Send method that take the parameters it need (title, recipients, message body⋯)
  • Add a SendThrough method to your entity that takes the server interface as parameter
  • Implement SendTrhough so that it computes values to pass to the Send method.

That’s it.

简单来说,就是定义一个服务接口及其所需的参数,然后在实体中添加一个ExecuteThrough方法,将参数和接口传入,在基础层等实现该接口:

public class Device
{
	public void ExecuteCmdThrough(string cmd, IDeviceService service)
	{
		//Do something "Domian" related before sending..
		//Dispatch behaviors to interface
		service.Execute(cmd);
	}
}