从广义的角度来说,Domain 指的是一个组织所从事的【领域】。不同行业的组织有着截然不同的领域,它们的认知范围和运营方式构成了自身的 domain。在设计的时候,应当尽可能的将不同的 domain 分离开来,形成 Core Domain、Supporting Domain 和 Generic Domain。
+ A Core Domain is a part of the business Domain that is of primary importance to the success of the organization.
+ 有时候为了支撑业务的运行,必须要创建 Bounded Context 来建立不可缺的模型,这就是 Supporting Subdomain。这些模型是不可缺的 (essential) 但并非最为核心的 (core)。
+ 另外,对于业务而言并不具有特殊性但是对于全局 (overall business solution) 而言是必须的部分可以被归入 Generic Subdomain。
书中给出的一个例子是 SaaSOviation 的 collaboration product,在设计的时候,这个虚拟的团队一开始将 `User` 和 `Permission` 这样的 domain 融入了 `Forum`、`Discussion` 之中,导致了不必要的概念引入,从而形成了不必要的 complicated code:
public class Forum extends Entity {
public Discussion startDiscussion(String aUsername, String aSubject) {
if (this.isClosed()) {
throw new IllegalStateException("Forum is closed.");
}
User user = userRepository.userFor(this.tenantId(), aUsername);
if (!user.hasPermissionTo(Permission.Forum.StartDiscussion)) {
throw new IllegalStateException(
"User may not start forum discussion.");
}
String authorUser = user.username();
String authorName = user.person().name().asFormattedName();
String authorEmailAddress = user.person().emailAddress();
Discussion discussion = new Discussion(
this.tenant(), this.forumId(),
DomainRegistry.discussionRegistry().nextIdentity(),
authorUser, authorName, authorEmailAddress, aSubject);
return discussion;
}
}
仔细推敲与 `Forum` 有关的概念就会发现,`User` 不同于 `Author`。在使用论坛的时候,`User` 是否具有合适的 `Permission` 并不是与这一核心概念相关的 concept,而 `Author` 才是。在以上的代码中以面向数据的调用访问了 user 的三个属性,而实际上这应当被封装到 `Author` 这个 value object 中。
为了解决这段代码所反映的问题,可以考虑这么几个措施——
1. 将 model 重构到 Responsibility Layers 中,通过将用户和权限 push down 到更低一层的 logical layer 来分离与 security 和 permission 有关的特性。不过这可能不是最好的方法,因为 Responsibility layers 主要用于大规模的模型 (large-scale models),尽管 layers 被仔细划分开,不过每一层 layer 最终都作为 Core Domain 的一部分存在于 model 中。
2. 另一个方法是 Segregated Core,首先要对 Collaboration Context 中所用到的所有与 security 和 permissions 相关的部分进行详尽的梳理,然后将 identity and access components 重构到同一 model 但是完全分离的 package 中。这样做可能不会产生最终分离的 Bounded Context,但是至少与这一目标更近了一些。
在分离了 indentity and access context 之后,Application Service clients 在访问 Core Domain 之前都要先使用 Segregated Core 内的对象来执行 security and permissions check。
public class ForumApplicationService ... {
@Transactional
public Discussion startDiscussion(
String aTenantId, String aUsername,
String aForumId, String aSubject) {
Tenant tenant = new Tenant(aTenantId);
ForumId forumId = new ForumId(aForumId);
Forum forum = this.forum(tenant, forumId);
if (forum == null) {
throw new IllegalStateException("Forum does not exist.");
}
Author author =
this.collaborationService.authorFrom(
tenant,
anAuthorId);
Discussion newDiscussion =
forum.startDiscussionFor(
this.forumNavigationService(),
author,
aSubject);
this.discussionRepository.add(newDiscussion);
return newDiscussion;
}
}
相应的 `Forum` 这一 domain class 修改如下:
public class Forum extends Entity {
...
public Discussion startDiscussionFor(
ForumNavigationService aForumNavigationService,
Author anAuthor,
String aSubject) {
if (this.isClosed()) {
throw new IllegalStateException("Forum is closed.");
}
Discussion discussion = new Discussion(
this.tenant(),
this.forumId(),
aForumNavigationService.nextDiscussionId(),
anAuthor,
aSubject);
DomainEventPublisher
.instance()
.publish(new DiscussionStarted(
discussion.tenant(),
discussion.forumId(),
discussion.discussionId(),
discussion.subject()));
return discussion;
}
}
修改之后的代码虽然仍不是漂亮的最终结果,但至少它已经分离了 `User` 和 `Permission` 所引入的纠结状态。经过重构之后的 `Forum` 代码更清晰地体现了 Collaboration Context 所关注的业务逻辑。
评论