post_gateway = BeanFinder.get('PostGateway')
row = post_gateway.get(post_id)
is_deleted = row['is_deleted']
return is_visible(is_deleted)
从领域逻辑的组织看单体应用与微服务架构
2015-07-17
马丁福勒在《企业应用架构模式》一书中阐述了领域逻辑的几种组织形式,事务脚本、表模块、领域模型和服务层。服务层虽然指的是抽象的代码组织形式,如果选择的服务层采用了 RPC 技术,它和微服务也基本没什么区别。
一个真实的例子
在微信朋友圈中,用户可以发布一些图片和短文分享给他的好友们。这些图片和短文我们称之为帖子。
原始需求:删除的帖子不能被好友们看到
用户可以删除他发表的帖子,帖子被删除后,他的好友们将看不到这个帖子。
我们设计一个『帖子』实体,命名为「Post」。帖子表有个字段 is_deleted
用来标识当前帖子的是否已被删除,譬如:
值 | 含义 | 可见 |
---|---|---|
1 |
|
否 |
0 |
|
是 |
我们分别采用事务脚本、表模块、领域模型等三种领域逻辑模式来实现这个需求:
-
事务脚本
事务脚本的数据源模式一般会采用表入口模式,示例代码如下:
-
表模块
表模块一般会采用行入口模式,示例代码如下:
finder = BeanFinder.get('PostFinder') post = finder.get(post_id) return is_visible(post)
-
领域模型
领域模型自然会采用活动记录这种模式,示例代码如下:
unitofwork = BeanFinder.get('UnitOfWork') post = unitofwork.get(Post, post_id) return post.is_visible()
领域逻辑模式 | 数据源模式 | 载体 | 输入 |
---|---|---|---|
事务脚本 |
|
函数 |
字段 |
表模块 |
|
函数 |
一条记录 |
领域模型 |
|
方法 |
无(对象本身/整个上下文) |
自从 2004 年 Rails 诞生以后,『活动记录』这种模式横扫天下,几乎所有的 FullStack 框架都支持基于 ActiveRecord 的 ORM 特性,这也从侧面反映出以上三种领域逻辑组织方式的优劣。
新需求:手机号认证
只有进行了手机号认证的用户发的帖子才对外可见。
我们设计一个『用户』实体,名为 User
。User
实体使用字段 is_verified
来标识用户是否进行过认证。
值 | 含义 | 可见 |
---|---|---|
1 |
|
是 |
0 |
|
否 |
根据以上设计,我们的示例代码如下:
领域逻辑模式 | 代码 | 变化 |
---|---|---|
事务脚本 |
示例
|
|
表模块 |
示例
|
|
领域模型 |
示例
|
|
纳尼?领域模型不需要做任何的调整么?当然不是,只是它在领域实体 Post 类内部把需求消化掉了!
怎么做到的?我们看一下 is_visible
内部的实现就明白了。
def is_visible(self):
user = self.user
is_verified = user.is_verified
is_deleted = self.is_deleted
return not is_deleted and is_verified
Note
|
self.user 是 ORM 的提供的加载特定外键关系对象的一种方法。
|
看起来领域模型这种组织领域逻辑的方式真是不错,不知道它能否搞定后面的需求。
新需求:软文帖子
随着朋友圈越做越大,用户越来越多,我们推出了一项增值服务,让用户可以付费推广他们的帖子:每个付费的帖子可以展示100次!
现在公司又来了一个团队,叫商业化研发团队,帖子的付费和展示统计都是由他们来完成,他们为帖子提供了一个 RPC 接口,可以查询付费帖子剩余的次数。
根据以上设计,我们的示例代码如下:
领域逻辑模式 | 代码 |
---|---|
事务脚本 |
示例一
|
表模块 |
示例二
|
领域模型 |
示例三
|
等等,你确定这次领域实体 Post 类也能把 PostCounterClient 内部消化掉么?当然,只是这样会有一些代价,引入更多的依赖。
def get_left_count(self):
client = BeanFinder.get('PostCounterClient')
return client.get(self.post_id)
一大波需求正在袭来
-
分享超过 1W 次的帖子不再可见
-
分享来自 Uber 的帖子不可见
-
用户举报超过 100 次的帖子不可见
-
被运营人员标记的帖子不可见
-
被识别为微商的帖子不可见
-
……
已不忍直视,我们的帖子实体 Post 类,至此已经依赖了统计系统、竞品系统、举报系统、运营系统、反垃圾系统等其他系统。
大家可以脑补一下 Post 类里面代码的样子。
再来一个压轴需求:自发自看
自己发的帖子只能自己看到
之所以称它为压轴需求,是因为这个需求引入了一个新的实体对象:当前用户。该实体的引入导致之前的接口都需要变,譬如:post.is_visible()
要改为 post.is_visible(current_user_id)
。