谈到领域驱动设计,简简单单的六个字,却包含了两个关键点:一个是“领域”,另一个是“设计”。“驱动”一词,则对两个名词(关键点)的关系进行了定义。左思右想,“驱动”一词好像是二者间关系一个现在看来最为恰当的描述,无可替代——既不是领域强制性决定如何去设计,又不是设计可以凌驾于领域之外的无关紧要,而是一种平行的,互为依托的关系。没有设计的领域毫无意义,而没有领域的设计也只是纸上谈兵。
似乎,对领域(模型)的设计,是对一件艺术品的仔细雕琢一般。
我开始学习领域驱动设计,源动力在于撸代码时的绝望。
对,就是绝望。
冗长而不知其意的名称,无穷无尽的 setter/getter。我仿佛一眼便看到了世界的尽头。就像现在你,不幸看到我这冗长、无聊、不知所云的开头一般。
就问你,累不累?烦不烦?绝不绝望?
绝望也要看下去。别问我为什么。从现在开始,在你的世界里,没有为什么。
好了,言归正传。
领域驱动设计中,最为关键的两个核心点,或者称之为支柱的便是 <strong>限界上下文</strong> 和 <strong>通用语言</strong>。
一直很奇怪老外的语法形式,又间接对国内整体翻译水平产生了怀疑。
在我看来,限界上下文倒不如称为 <strong>语境</strong>(上下文是具象的,语境才是抽象的好不好?)。
通用语言,就是在语境中能说得通,说顺溜,不会产生明显歧义(怪我大中华文化博大精深咯)的一个词、一句话。
看到这里,你应该觉着这玩意儿时骗人的吧。这样想就对了,没这样想就试着想想。
其实,一开始我也是这么想的。一个词、一句话、一段文,外加一个语境,就能搞出什么鬼设计来?
还真别说,举个简单的例子,亮瞎你的 24K 纯钛合金 dog 眼:
试着设计一个我们常见的Class:User
public class User {
private String username;
public void setUsername(String username) {
this.username = username;
}
public String getUsername() {
return this.username;
}
}
试着用一个 User 实例化对象调用他的 getUsername()方法:
public class Main {
public static void main(String[] args) {
User user = new User();
String username = user.getUsername();
}
}
传统的调用方法大概就是这样吧。
不论其他,请您给我顺畅地用中文将 user.getUsername() 翻译过来。
我的翻译是这样的:用户(user)得到(get)Username(用户名)。
so easy, <strong>用户得到用户名</strong>。
发现问题没有?没有?回幼稚园重新回炉深造去。好吧,开个玩笑,重来一次,还是上面的模式:将 User 替换成 You:
public class You {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
试着用一个 You 实例化对象调用他的 getName()方法:
public class Main {
public static void main(String[] args) {
You you = new You();
String name = you.getName();
}
}
首先声明,以上这个例子只在于说明问题。
就问你,发现问题没?还没发现?对不起,不开玩笑的说,请您出门右转,祖国的花儿幼稚园欢迎您!
慢走不送,下次也别再来了。
什么鬼嘛?<strong>你得到名字</strong>。
你需要得到你的名字吗?需要吗?需要吗?需要吗?
不需要!
你的名字是你出生后父母给你起的,中间即使可能更名(换姓的请举手),但你从来都不需要去“得到你的姓名”。除非你患上了“失忆症”。但,即使如此,你也应该调用 AmnesicPatients#whoami()。
好,话题继续回到领域驱动设计上。说到底,领域驱动设计就是用来解决不合理:包名不合理、类名不合理、属性名不合理、方法名不合理、方法位置不合理、调用不合理、程序奔溃不合理......
没有包治各种不合理(起码李家孩子和隔壁老王同姓这种不合理是真没辙),毕竟,凡事都有妥协与余地,但总归大部分不合理可以得到根治。况且,一切都合理才是最大的不合理。
继续 User 这个例子,这个是改进版的:
public class User {
private String username;
public void setUsername(String username) {
this.username = username;
}
public String username() {
return this.username;
}
}
public class Main {
public static void main(String[] args) {
User user = new User();
user.username();
}
}
现在的调用是不是合理多了:<strong>用户的用户名</strong>
public class You {
private String name;
public void setName(String name) {
this.name = name;
}
public String name() {
return this.name;
}
}
public class Main {
public static void main(String[] args) {
You you = new You();
you.name();
}
}
<strong>你的名字</strong>
别问我“的”是哪儿来的, ‘.’ 即是“的”,“的”即是 ‘.’ 。
不要怀疑,即使将现在的论调拿到过去旧的案例,旧案例的调用依旧不合理:用户的得到用户名(得到用户名的用户)、你的得到名字。
八格牙路。你的良心大大地坏了。
其实,初识 DDD(Domain Drive Design · 领域驱动设计)觉得最难的也就是对什么“限界上下文”(语境)和“通用语言”(在某一语境中尽最大可能无歧义的词、句)了。所以......
上面讨论的,其实基本都是属于通用语言范畴,至于语境(限界上下文),我真不知道该怎么描述,难道是,见人说人话,见鬼说鬼话——同样的人,在不同的环境下,会有不同的表现形式。一个同样名称的 Class,在不同的业务系统中就有可能描述了不同的两种事物?
好吧,贱人贱智了!
《实现领域驱动设计》中,谈到通用语言是,业务专家和开发者等一群对相关业务或技术熟悉的人,进行反复讨论后,互相补充后达成一致的产物。
所以说。。。那啥,刚才出门右转的朋友,你可以学成归来了。
PS:行文风格有点骚(我这老脸),不喜轻喷!