领域驱动设计 - 在你开始之前清理一下你的观念

-------------------------------------之前园子里面有人翻译过,在家闲的没事又整理了一遍。

Start Developing a New Application 

开始一个新的应用程序
 
What we traditionally do when we start a business application? We read the spec and find the functionalities. We break down tasks. In most of the cases the goal of the breakdown is to come up with an estimation and plan of works. We do the estimation. We distribute the works among team members. We design the database schema - sometimes by the team leader or sometimes by the respective developer. We start coding.
So?  What’s wrong with this approach? We have been doing good! Don’t we? 
The answer is YES and NO! Yes we are doing good in delivering our projects. But NO! We are not doing good in maintaining and extending our projects!


 当我们开始一个传统的商业应用程序时我们通常都做些什么?我们阅读说明和查找相应的功能,我们分解任务,大多数情况分解的目标是形成可行性分析和工作计划。我们进行可行性分析,给团队人员分配任务。在团队领导和其他开发人员的协作下我们设计数据框架,我们开始编码。

然后呢?这样做有错吗?我们这样做的很好,不是吗?回答是Yes和No. yes是我们这样做很的实现我们的项目,但是No我们没有很好的维护和扩展我们的项目。

Think about all of the projects you have worked last few years in the traditional approach. Did you ever face any of the issues below?

  1. Your project has the same functionality implemented in the same way or different in different places.
  2. You have more than one object for the same item.
  3. You have objects that have properties that are not actually attributes of that object.
  4. You have no or very poor relationship among related items.
  5. Looking at your objects it is not possible to understand what actually the whole application is all about.
思考一下在传统开发模式的这些年的项目中,你有没有遇到以下问题?
    1. 你的项目中以相同或不同的的方式实现了相同的功能在不同的地方。
    2. 你对同一个项目用了多个对象
    3. 你的项目存在不属于该对象实际属性的对象。
    4. 你的项目没有或很少的存在相应的关系。
    5. 看你的对象很难理解整个整个应用程序义。
 
I am sure you have been facing these issues regularly. But do you know why?  The reason is, traditional approach do not guide us designing the system in Up to Bottom fashion. Rather it tempted us to design the system in Bottom-Up fashion. See, when you design a system you need to know what as a whole the application will do? What is the goal the client is trying to achieve? Then, from the top level goal you come up with different smaller functionalities that will eventually allow the users to achieve the top level goal. 

But when you design in bottom-up approach, you first design for the granular functionalities, and you have little or no knowledge how this functionality will be used from the Top level and how the Top level functionalities will actually look like.

Have you ever heard that a developer of your team is talking like he does not have the domain knowledge of the whole application? Perhaps yes! I think you can understand the reason. Cause, the design of the application does not represent the domain of the system. And so, developers know only the portions they worked. This is Sad! Isn’t it? 

So, is traditional approach – “Designing the application starting from database” a throw away concept?  Not really! But if you have a complex application to develop, this bottom-up design approach does not dictate you to come up with a proper object oriented design. 

 我确定你已经经常的面试以上这些问题,但是你知道为什么吗?原因是,传统的开发模式不会引导我们自下而上的方式去设计系统。相反它会诱导我们自下而上的方式去设计系统,可见,当我们设计系统时需要知道应用程序想要做什么?什么是客户想要实现的目标?然而,从顶层目标来看你拿出不同的小功能来最终将实现所有的用户来实现最终顶层的目标。

当我利用自下而上的方式设计时,你第一个设计的是粒状的功能模块,你很少知道或不知道这个功能从顶层如何被使用和顶层的功能实际看起来怎么使用。

你是否听说过你团队开发成员的谈话,就像他不具备整个应用程序的领域知识?或许是的,我想你应该知道原因了,理由,应用程序的设计不代表系统的领域。因此,开发人员仅仅只知道自己工作分配的部分,这是很可悲的,不是吗?

那么传统开发模式-“应用程序设计从数据库开始”一个被遗弃的概念?并不是的,但是假如你开发复杂的应用程序,这种自下而上的开发设计模式对你来说并不是真正意义的面向对象的设计。

What is the solution then? 

The solution is DDD (DOMAIN DRIVEN DESIGN).

What is DDD?

Domain-driven design is not a technology or a methodology. DDD provides a structure of practices and terminology for making design decisions that focus and accelerate software projects dealing with complicated domains.
- Wikipedia
该如何解决呢?
解决方案是DDD(领域驱动开发)

什么是 DDD?

领域驱动开发并不是一个技术或一个方法。DDD提供了一种实践结构和设计决策用于复杂性业务场景的设计。

Concepts to cover in this article:

  1. Understanding the Domain.
  2. Ubiquitous Language.
  3. Contexts and Bounded Contexts.
  4. Entities and Value Objects.
  5. Aggregates and Aggregate Roots.
  6. Persistence Ignorance. 
  7. Repository. 
  8. Domain Service. 

In this article I will try to avoid becoming too technical, rather I will try to go through different concepts of DDD being close to the real world.  I will try not to show any code here. Because I believe if you understand the concept and starts thinking in DDD way, implementation is easy. The toughest part is to tune your thinking process!

本文中涉及的概念:

     1、了解领域。
     2、统一语言。
     3、上下文和界定上下文
     4、实体和值对象。
     5、聚合和聚合根。
     6、仓储
     7、领域服务
在这篇文章中我尽量避免变得太技术,而我将试着通过DDD不能的概念让它更贴近现实世界。我将试着不展示太多的代码在这里。因为我相信如果理解这个概念并且开始思考DDD,实现很简单,最难的部分是调整的思维过程。

Understanding the Domain

A sphere of knowledge, influence, or activity. The subject area to which the user applies a program is the domain of the software.
- Wikipedia

Do you get a feeling what is domain from this definition?  Can you tell what is the domain of the project you are working on at this moment? Can you tell what is the domain of the famous website YouTube?

In this article I would like to go through a real world example to give you the feeling how to start analyzing your project driven by your domain. This example may not be related with application development but as the goal is to tune our thinking top to bottom manner, it will be useful. But again, we will go through the technical terms of DDD too! 

Let’s say you are engaged to design a building. The requirement is:

  • You have a defined amount of land
  • Your building will have 6 floors.
  • Each floor will have 4 apartments.

What is your domain here?

The domain is Building(?). It could be. But note that, if you consider Building as your domain you may miss few granular details for your requirement. The building you are going to design must have design for apartments where people will live. So, a general term “Building” can make us miss few details. So, we may narrow down our domain to “Residential Building”.

Now, when you talk about your work with engineers and also with the people who engaged you to design the building, the term “Residential Building” is more meaningful for everybody concerned. Did you mark very small change in language here? The contractor is telling you to design a building where there will be 4 apartments in each of the 6 floors. Now, if you send an engineer to the site telling him we will need to construct a building there, they might not consider many attributes that a residential building must have. On the other hand if you use the term “Residential Building”, most likely he will come with a valid analysis.
This is how we come to an “Ubiquitous Language”.

了解领域

知识、影响或活动的领域,用户的应用程序领域是软件的学科领域。
- Wikipedia
从这个定义来说,你是否感觉到什么是领域?你能否谈下现在你工作的项目当中什么是领域?可否谈下著名的网站YouTube的领域?
 
在这本文我想通过一个现实世界例子给你带来一种感觉,如何开始使用领域驱动设计来开始分析你的项目。这个例子可能不会关系到应用程序开发,但是它会从上到下调整我们的思维方式为目标。它是非常有用的,但同样,我们将贯穿DDD的专业术语。
比如说你现在正在设计一个建筑物,需求如下:
  •       你有一片土地
  •       你的建筑要盖6层
  •       每层都将有4个公寓
      这里的领域是什么?
建筑是领域?它可能是,但是要注意的是,如果你把建筑做为你的领域的话在你的需求里可能会错过一些颗粒状的细节。在设计建筑的时候你必须先为人们生活住的公寓设计 。所以,一个通用的术语“Building”就让我们忽略了选用多细节。所以, 我们可能要对我们的领域缩小到“住宅楼”。
现在,当你跟工程师们谈论你的工作和跟人们谈起谁让你设计这个建筑时,大家对“住宅楼”这个术语应更加关注更有意义些。在这你有没有注意到语言上的一个小的变化?承包商告诉你设计一个每层都有4个公寓的6层楼的建筑,现在,假如你派一个工程师到现场告诉他这需要建一栋楼,他不会去考虑太多关于住宅楼所需的一个属性。另一方面,假如你用“住宅楼”术语,很大可能他将拿出一个很好的分析出来。这就是我们为什么引入“统一语言”。

Ubiquitous Language

The concept is simple, that developers and the business should share a common language that both understand to mean the same things, and more importantly, that is set in business terminology, not technical terminology.

通用语言

这个概念很简单,开发者和企业应该共享一个共同的语言意味双方都在同一件事件上能够达成共识,非常重要的,这是设置商业术语,不是技术术语。
 

通用语言的一些实例

 例 1:
  错误的语言
 小房间的宽度和长度的比例估计4:3.
 正确的语言:
 孩子的房间的长度约20英尺、宽度约15英尺。
  例 2:
 让我们从软件的角度来看这个例子。
 错误的语言:
 在查找时候我们会考虑到sql server的分析器和词典使查找更加有效。除些之外我们在查询的时候排除那些不用的词这样使它更加准确。注意的是,你的领域专家可能不是一个技术的人因此他可能不知道“分析器”,“词典”,“停用词”词的意思。
正确的语言:
 在查询功能的时候我们会考虑到搜索短语的同义词所以它不排除相关的搜索结果。此外,我们也不会通过编号来区分任何搜索词(单数或复数),
时态,分词等不区分任何搜索词。同时所有的搜索都跟预期一样,我们将忽略所有不具有任何搜索结果的干扰词。那些干扰词可能是“am”,"but","where","about" 等等。
 在这里你看到语言上的不同了吗?一个正确的语言可以使所有参与人的想法和思考方式相同。
 让我们回到"住宅楼"领域。看,你可以开始把住宅楼设计作为一个单一任务,然后一起解决所有的事情。但是真的会按照预期的方式去做吗?需要注意的是,如果你只是考虑到这项工作只是一个单一的工作单元你可能会错过很多东西。设计一个建筑关联到很多东西。例如:你必须考虑通风,实用,公园,公用设施等等。
 现在你看,不同的其他情况都来了,这怎样的概念"上下文"和"界定上下文"出现在领域驱动开发。

上下文和界定上下文

 一个界定上下文可以当作是一个小型的应用,界定上下文包含自己的领域,自己的代码和持久化机制,那里逻辑一致性;每一个界定上下文独立于其他界定上下文。
一些界定上下文的例子:
  想想一个电子商务系统,最初你可能说它是一个购物上下文的应用,但是,如果你更加仔细,你会发现其他更多的上下文。像:库存,运输,帐户等。
划分不同界定上下文之间的大型应用将让你的应用更加的模块化,将帮助你区分不同的关注点并且使应用简单的管理和提高,每一个界定上下文都明确职责,并且可以在半自动的方式下运作,通过拆分这些分开的上下文,使得逻辑更加合理明显,并且你可以避免BBOM(大泥球)
什么是BBOM?
 大泥球就是一个混乱的结构,无序,懒散,错综复杂,随意拼接。这些系统看起来已经失控的迹象,和复用,维护。
 
Information is shared promiscuously among distant elements of the systemoften to the point where nearly all the important information becomes global or duplicated. The overall structure of the system may never have been well defined.(不知道怎么翻译),
- Brian Foote and Joseph Yoder, Big Ball of Mud. Fourth Conference on Patterns Languages of Programs (PLoP '97/EuroPLoP '97) Monticello, Illinois, September 1997
  我们的时间目标就是避免BBOM.
  我们再谈"住宅楼领域",可能看到,我们已经有几个界定上下文:
    •      电力供应
    •      停车场
    •      公寓
    •      等等
  让我们谈一下公寓,公寓是基本是由几个不同房间组织的,房间应该包含不同元素例如窗户,门等等,现在我现在有两个问题关于房间的窗户。
        问题1:你想象过一个没有窗户的房间吗?
        问题2:居住在房间里的窗户是否应该有一个唯一标识呢?
  下面陈列DDD的概念将回答这些问题
    1.     实体
    2.     值对象
    3.     聚合和聚合根

    实体

   ”这是我的实体,那里有好多很像它,但是这个是我的。“
 定义一个实体的关键特性是它有一个标识,它是系统中是唯一标识一个实体,不管有多相似,相同的实体除非它有相同的标识。 
 例如:
    • 你在公寓的房间
    • 在Facebook的联系人
    • 在CodeProject的文章

    值对象

  定义值对象的关键特性是它没有标识,OK,也许有点简单化,值对象的意图仅仅是表示一些事物的属性,两个值对象可能具有相同的属性,在这种情况它们是相同的,不凭借自己的其他属性它们是无论如何都不会有任何值,另外值对象的常用的方面是它们或许是不变的,一但被创建它们将不能改变或修改,你可以创建一个新的值对象,并且它们没有标识,这仅仅是作为变化的另一个。
 例 :
    房间里的窗户
    你网站一些人的地址
    你的搜索条件
 备注:一个值对象变成一个实体依情况而定。你能找到类似这样的场景吗?如果你的应用程序的搜索功能的要求说,搜索条件需要保存在数据库中并且用户可以从保存的搜索条件列表中选择相同的搜索。
现在你知道什么是实体和值对象在DDD中的意义。在领域驱动设计实体和值对象都是独立存在的。但是在大多数情况下关系可以是这样的,一个实体或者值对象在没有上下文的情况是没有价值的。
 例:
    •  如果那一个空间窗户才会被定义。
    •  如果下单了订单详情才会存在。
    •  如果问题被提问了才会有问题描述。
 非常简单难道不是吗?相信我,现在你将知道在DDD中什么是聚合和聚合根。

    聚合和聚合根

  在以上给出的例子中-
    •  房间,订单和问题是我们的聚合根。
    •  另一方面说,订单详情和问题描述是我们的聚合。
  “一群聚集对象作为数据变更的单元。”
 集群的所有对象都应作为一个聚合。
 所有的外部访问,集群通过一个单一的根实体,根实体被定义为聚合根。
例:
  1.  问题的描述将不会被保存除非相应的问题被保存。
  2.  问题的描述将不会被检索除非相应问题被检索
这里的问题就是聚合根,问题描述就是聚合。在DDD的概念中聚合和聚合根是非常重要的。
到目前为至,我们已经讨论过了关于领域,对象/实体,上下文,聚合等等,数据库是怎么样的呢?我们是不是错过了什么?是不是有些东西应该在设计了?
回答是No,DDD是一个隐式持久化的。

Repository commonly refers to a location for storage, often for safety or preservation.
- Wikipedia

    隐式持久化

在领域驱动设计中你的目的是创建一个领域模型。你需要去定义你应用程序中想要去完成的项目(对象)的功能。你需要去标示他们不同对象之间的关系和彼此之间是怎么相互影响的。你需要找到如果用领域模型你的客户业务目标是可以实现的。这里那里是数据库的存在呢?你不需要知道在你的领域中数据持久化是怎么著在那实现的,即使是当你做的领域模型数据持久化时。关于你的持久化的隐式方法将领域模型从应用程序的持久层耦合中释放出来。这最终将关注的持久化和你领域模型沟通机制区分开。 结果你的应用程序将不和任何数据耦合并且会非常简单的单元测试。
但是,在真正应用程序中你需要一个数据库,但是你的领域模型不需要了解它。所有引入了“仓储”是最终管理你应用程序的持久化关注。

    仓储

你可以告诉我英语中的“Repository”是什么意思吗?
Repository通常的用于储藏东西的场所。常常为安全或受保护的。
正如我已经说过的你的领域模型将不知道任何数据库。它知道些什么呢,这里系统中的仓储将负责储存你的数据并且检索数据。你的领域模型是没有办法关心如何以及在何时对数据进行持久化的。因此,它可以在sql server \oracle \xml\text file别的东西,我希望你在DDD中对仓储有一个意识。
让我们变的更多一点技术。
仓储介质在领域和数据映射之间使用集合类接口,用于访问域对象。对于你的数据存储它更像一个正面,假装你的领域集合。
仓储不是一个数据访问层。
需要注意的是仓储从不关注“数据“,它讨论聚合根。你可以告诉你的仓储去添加一个聚合根在它的集合里面或你可以要求它为特定的聚合根。如果你还记得聚合根可以包含一个或多个实体和值对象,这使得它完全不同于传统的DAL,从数据库表中返回一行数据。

    仓储的实施策略

正如我说的,仓储在DDD中是处理持久化的设计模式,这种模式的细节已经超出了本文的范围,然而,这里我试着最小告诉大家我们如何完成仓储的实现。
1、第一步你有一个接口--IRepository 必须通用
2、你将有一个抽象类实现IRepository接口。
3、你将有一个INhRepository接口用于你的持久化机制它将继承IRepository
4、你将有一个实现INhRepository的像:”NhRepository“
5、最好你可以有一个通用的类实现Repository默认实现Respository的所有通用方法
6、像NHGenericRepository继承NhRepository和实现IGenericNhRepository
7、你有一个特别的仓储用于你的聚合根,扩展NHGenericRepository
8、你的应用程序将使用服务定位器找到你应用程序将使用的仓储

    领域服务

领域服务在DDD中是另外一个很重要的概念。假如实体和值对象是”事件“在你的领域,服务则是应对动作、行为和活动的另外一种方式。
应该不是逻辑上的直接实体?
Yes,它确实是,我们要我们的模型实体的逻辑关系到他们以及他们的孩子,但是当我们需要处理复杂的行为或者外部行为可能需要我们在聚合根暴露一些动作对外部世界。这就是为什么不同的聚合根创建一个领域服务是一个很好的主意。你可以考虑领域服务作为业务逻辑层和领域的操作层。

End Words 

In this article I have tried to introduce the basic concepts and terminologies of Domain Driven Design with examples of real world. The goal was to make you feel comfortable with DDD world. But really developing applications with DDD is a big challenge. The more you love and practice DDD concepts while you design your object model, the more accuracy you will gain in your design. As I said before the most important thing is, you must think in Domain Driven Way. If you don't you will hugely suffer when your application is a real complex one.

你可能感兴趣的:(设计)