关键词:
前言
基于 DDD 传统分层架构实现。 项目 github地址:https://github.com/WuMortal/DDDSample
这个分层架构是工作中项目正在使用的分层架构,使用了一段时间发现受益匪浅,所以整理好我对该分层架构的一些理解分享给大家,我对于该分层架构还处于学习阶段理解有误的地方请指出。本次会以一个案例来说明各个分层的作用以及他们之间的调用关系,还有本次的重点不在于DDD
,因为这个我还未能完全理解,当然避免不了中间会涉及DDD
的一些概念。
DDD 简单介绍
DDD
什么?为什么使用 DDD
?
关于这个问题有兴趣的可以自行百度,我相信网络上已经有大量的文章来说明这几个问题。我目前的理解是“业务”,是为了应对现在复杂和多变的业务,是一种开发理念。
这里我就以一个小故事描述吧,有一天你接到任务要实现一个修改用户的功能,非常简单。使用传统三层架构我们会怎么写?
-
先在
DAL
层添加UserDAL
然后实现一个Update(UserEntity user)
方法 -
接着在
BLL
中添加一个UserBLL
在实现一个Update(string email,string pwd ...)
方法。 -
UI
层在调用,OK 完成任务下班回家。
接着你接到一个新的需求就是:需要增加用户修改信息的记录。
你立马在 BLL
的 Update
的方法里增加的用户修改信息的操作记录,完成需求。
过了一段时间又来了一个需求:用户改了信息需要通知到管理员,并且用户每天只能修改 3 次信息。
好了之后又经历了几波需求,你的代码也在不断的增加和变化,有一天你接收新的项目或者离开了,那么接收你项目的人完全不清楚这里的业务情况。因为 Update
方法并没有直接的反应出里的业务情况,代码目的不明确。代码变得难以维护。
那么在 DDD
里这些应该怎么做呢?
-
首先在方法的命名上做出更改既然业务是修改信息那么命名应该是
Modify(string email,string pwd ...)
-
将用户修改信息的记录代码放在
DomainService
(领域服务) 中,当然这里的类、方法命名要直接的反应出业务情况,如:RecordUserModifyDomainService
。 -
对应的通知管理员的代码也应该放入
DomainService
中,DomainService
应该尽量简单一般只做一件事情。
分层架构图
下面是关于 DDD 分层的一些描述,摘抄至之前看过的一片文章。
Presentation 为表示层,负责向用户显示信息和解释用户命令。这里指的用户可以是另一个计算机系统,不一定是使用用户界面的人。
Application 为应用层,定义软件要完成的任务,并且指挥表达领域概念的对象来解决问题。这一层所负责的工作对业务来说意义重大,也是与其它系统的应用层进行交互的必要渠道。应用层要尽量简单,不包含业务规则或者知识,而只为下一层中的领域对象协调任务,分配工作,使它们互相协作。它没有反映业务情况的状态,但是却可以具有另外一种状态,为用户或程序显示某个任务的进度。
Domain 为领域层(或模型层),负责表达业务概念,业务状态信息以及业务规则。尽管保存业务状态的技术细节是由基础设施层实现的,但是反映业务情况的状态是由本层控制并且使用的。领域层是业务软件的核心,领域模型位于这一层。
Infrastructure 层为基础实施层,向其他层提供通用的技术能力:为应用层传递消息,为领域层提供持久化机制,为用户界面层绘制屏幕组件,等等。基础设施层还能够通过架构框架来支持四个层次间的交互模式。
说明
如上图每个层中其实对应着具体的项,下面将对每个项进行说明。
-
Domain
层分为:Domain
、DomainService
和IDomainService
。- 首先
Domain
中包含有Entity
和IRepository
,Entity
是你的实体一般对于数据库表但是在某些情况下你也可以冗余一些字段。IRepository
仓储的方法的定义,该层不会有具体的实现。 DomainService
和IDomainService
,IDomainService
只是负责表达业务的概念,DomainService
里才是具体业务逻辑代码。在这一层的代码命名上需要注意,我们的命名一般要能直接描述出该代码业务的功能。这里可以参考DDD
的几个概念:通用语言、领域。
- 首先
-
Infrastructure
层分为:Repository
和CrossCutting
:Repository
里面就是Domain
里IRepository
的具体实现。项目中 RepositoryExtensions.cs 是一个扩展类,将所有的仓储注入容器中,方便我们在项目中使用DI
(依赖注入)。CrossCutting
主要是提供一些各个层通用的东西,如一些枚举、扩展方法、工具类等等。
-
Application
层分为:Application
和ApplicationContract
。ApplicationContract
里主要包含DTO
、ViewModel
、IXXXService
。DTO
是数据传输对象,主要负责给展现层提供展示数据,DTO
里应该只有值类型存在,当然根据具体情况也可存在其他的DTO
。ViewModel
用于展现层传入的模型,简单的说DTO
输出,ViewModel
输入。IXXXService
就是应用层的方法定义。Application
里面主要是用于 实现ApplicationContract
里的IXXXService
,还有Entity
和DTO
的映射也属于该层的工作。ApplicationExtensions.cs 扩展方法是用于实现DI
。
-
Presentation
层里目前只有一个 WebAPI。展现层的代码一般有:对传入模型的校验。
案例
本次以一个用户注册的流程为案例,来简单说明如何使用该分层架构进行项目开发。
- 首先在
Domain
中建一个 UserEntity,有 Id、Mobile、Name、Age、RegisterDateTime 属性。接着建立 IUserRepository,编写需要定义的方法,这里我定义了一个 GetByMobile(string mobile) 方法。
1 [Table(Name = "User")] 2 public class UserEntity 3 4 [Column(IsIdentity = true)] public Guid Id get; set; 5 6 public string Mobile get; set; 7 8 public string Name get; set; 9 10 public int Age get; set; 11 12 public DateTime RegisterDateTime get; set; = DateTime.Now; 13 14 15 public interface IUserRepository : IBasicRepository<UserEntity, Guid> Task GetByMobileAsync(string mobile);
IBasicRepository 是使用了 FreeSql,你们可以自己实现。
- 然后在 Repository 中建 UserRepository 类,该类继承 IUserRepository 并且实现该接口的所有方法。
public class UserRepository : GuidRepository, IUserRepository public UserRepository(IFreeSql freeSql) : base(freeSql) #region Implementation of IUserRepository public async Task<UserEntity> GetByMobileAsync(string mobile) return await this.Where(u => u.Mobile == mobile).FirstAsync(); #endregion
- 仓储基本好了后就是
Application
,首先需要在 ApplicationContract 中建 UsesDTO,根据业务情况你也可以建 UserSimpleDTO 、UserDetailDTO。DTO
里包含你需要返回的数据,我这里有 Id、Name、Mobile、Age、ProfilePhotoSrc(头像地址根据 Id 拼接,这里我用 imgage/Id.png 的格式)。
public class UserDTO public Guid Id get; set; public string Name get; set; public string Mobile get; set; public int Age get; set; public string ProfilePhotoSrc get; set;
- 添加好 UserDTO 后,然后添加 IUserService.cs 接口,接着在 Application 的 Service 中添加对应的 UserService,并且 UserService 继承 IUserService。
public interface IUserService /// /// 用户注册 /// ///用户名 ///手机号 ///年龄 /// Task Register(string userName, string mobile, int age); List<UserDTO> GetList(); public class UserService : IUserService readonly IUserRepository _userRepository; public UserService(IUserRepository userRepository) _userRepository = userRepository; #region Implementation of IUserService /// <summary> /// 用户注册 /// </summary> /// <param name="userName">用户名</param> /// <param name="mobile">手机</param> /// <param name="age">年龄</param> /// <returns></returns> public async Task<bool> Register(string userName, string mobile, int age) var userEnity = await _userRepository.GetByMobileAsync(mobile); if (userEnity != null) return false; var addUserEntity = new UserEntity Id = Guid.NewGuid(), Age = age, Name = userName, Mobile = mobile ; return await _userRepository.InsertAsync(addUserEntity) != null; public List<UserDTO> GetList() return _userRepository.Select .ToList().ToDTOList(); #endregion
-
UserServcie 是对应展现层的控制器 UserController ---> IUserService。
-
最后展现层的 WebAPI 只需要注入 IUserService,就可以开心的使用了。
[HttpPost] public async Task Post() var second = DateTime.Now.Second.ToString("00"); bool isSuccess = await _userService.Register("Wigor", $"188888888second", 22); return Ok(isSuccess);
就这样这个简单的案例就完成了,你可以参考着上面 说明 对比着去看看,当然这里有一些东西并没有体现,如 DomainServie,如果按照 DDD 来说还有 值对象、聚合、通用语言……,对于「通用语言」的话其实上面的小故事就体现出了一点。
结语
就 DDD 而言我这里还有很多东西都没有交代,今后有时间的话会慢慢的写出来。还有我也是在学习 DDD 所以有错的地方请指出,望多多包涵。
在使用这套分层架构的时候碰到了许多问题,这里还要感谢老大的指导,为我解答疑问。
最后附上《实现领域驱动设计》中的一句话:
我认为不管使用什么技术,我们的目的都是提供业务价值。
ddd领域驱动设计实战(分层架构)(代码片段)
...分类1.3分层架构演进2各层职责2.1用户接口层2.2应用层2.3领域层2.4基础层3微服务架构演进微服务架构的演进案例微服 查看详情
解构领域驱动设计:领域驱动设计的核心之分层架构(代码片段)
...DDD分层架构的核心思想就是将所有业务规则的代码抽取到领域层,保证领域层的编码与领域模型是完全一致的。下图是DDD的分层架构。?我将通过代码来演示这个新的分层架构。1应用层应用层在这里非常的简单清晰,它仅仅是将... 查看详情
ddd领域驱动设计:四层架构应用(代码片段)
...、DDD四层与传统三层区别二、四层架构详解1.分层作用2.领域对象三、编码实践1.代码结构四、常见问题1.领域模型(充血模型)注入问题结尾前言分层架构是运用最为广泛的一种架构模式,几乎每个软件系统都需要通... 查看详情
ddd领域驱动设计:四层架构应用(代码片段)
...、DDD四层与传统三层区别二、四层架构详解1.分层作用2.领域对象三、编码实践1.代码结构四、常见问题1.领域模型(充血模型)注入问题结尾前言分层架构是运用最为广泛的一种架构模式,几乎每个软件系统都需要通... 查看详情
ddd领域驱动设计初探:聚合(代码片段)
前言:又有差不多半个月没写点什么了,感觉这样很对不起自己似的。今天看到一篇博文里面写道:越是忙人越有时间写博客。呵呵,似乎有点道理,博主为了证明自己也是忙人,这不就来学习下DDD这么一个听上去高大上的东西... 查看详情
ddd领域驱动设计实战-分层架构及代码目录结构(代码片段)
...现出自何处。[](()1.3分层架构演进[](()1.3.1传统四层架构将领域模型和业务逻辑分离出来,并减少对基础设施、用户界面甚至应用层逻辑的依赖,因为它们不属业务逻辑。将一个夏杂的系统分为不同的层,每层都应该具... 查看详情
ddd(领域驱动设计)从入门到精通
参考技术A一、DDD领域驱动设计-基本原理1、DDD领域驱动设计-入门介绍2、DDD领域驱动设计-基本概念 2.1.DDD领域驱动设计-领域和子域 2.2.DDD领域驱动设计-限界上下文 2.3.DDD领域驱... 查看详情
ddd领域驱动设计(代码片段)
有幸参与了一些领域驱动的项目,读了一些文章,也见识了一些不伦不类的架构,感觉对领域驱动有了更进一步的认识。所以今天跟大伙探讨一下领域驱动设计,同时也对一些想要实践领域驱动设计却又无处下手,或者一些正在... 查看详情
领域驱动设计(ddd)架构演进和ddd的几种典型架构介绍(图文详解)(代码片段)
👇👇关注后回复 “进群” ,拉你进程序员交流群👇👇作者丨我思知我在来源:https://blog.csdn.net/qq_32828253/article/details/110673205我们生活中都听说了DDD,也了解了DDD,那么怎么将一个新项目从头开... 查看详情
去哪儿网领域驱动设计(ddd)战略战术设计实战
...战 1:25:56P8 07-DDD整合分层架构和微服务设计40:29P9 CQRS在领域驱动中的重要作用 43:22P10机票辅营DDD的分层架构设计 1:14:02https://www.bilibili.com/video/BV1Fv41157kW?p=1 查看详情
领域驱动设计(ddd)分层架构的三种模式
...plication为应用层,定义软件要完成的任务,并且指挥表达领域概念的对象来解决问题。这一层所负责的工作对业务来说意义重大,也是与其它系统的应用层进行交互的必要渠道。应用层要尽量简单,不包含业务规则或者知识,而... 查看详情
ddd实践_如何使用ddd设计代码模型
...微服务一级目录结构三、各层目录结构四、注意事项五、领域对象的整理六、从领域模型到微服务的设计七、领域层的领域对象八、应用层的领域对象九、领域对象与微服务代码对象的映射DDD并没有给出标准的代码模型,因... 查看详情
asp.netcore系列63领域模型架构eshoponweb项目分析上(代码片段)
...述 本篇继续探讨web应用架构,讲基于DDD风格下最初的领域模型架构,不同于DDD风格下CQRS架构,二者架构主要区别是领域层的变化。架构的演变是从领域模型到CQRS, 一开始DDD是用领域模型的分层架构,用单一的领域模型处... 查看详情
谈谈ddd(领域驱动设计)(代码片段)
...组织了小红花的新一期分享快速搞定数字化项目——采用领域驱动设计(DDD)建设一个电商平台,听完池总的这个分享之后,我终于是把这两年重新热起来DDD(以下称为现代DDD)和我十几年前熟悉的DDD(以下称为... 查看详情
去哪儿网领域驱动设计(ddd)战略战术设计实战
P1 00-前言:走入DDD 31:30P2 01-大白话聊DDD 49:29P3 02-方舟DDD实践(上)56:44P4 03-方舟DDD实践(下) 1:13:19P5 04-战略设计 1:13:08P6 05-战术设计1:21:08P7 06-酒店报价DDD实战 1:25:56P8 07-DDD整合分层架构和微服务设计40:29P9... 查看详情
去哪儿网领域驱动设计(ddd)战略战术设计实战
P1 00-前言:走入DDD 31:30P2 01-大白话聊DDD 49:29P3 02-方舟DDD实践(上)56:44P4 03-方舟DDD实践(下) 1:13:19P5 04-战略设计 1:13:08P6 05-战术设计1:21:08P7 06-酒店报价DDD实战 1:25:56P8 07-DDD整合分层架构和微服务设计40:29P9... 查看详情
谈谈ddd(领域驱动设计)(代码片段)
...组织了小红花的新一期分享快速搞定数字化项目——采用领域驱动设计(DDD)建设一个电商平台,听完池总的这个分享之后,我终于是把这两年重新热起来DDD(以下称为现代DDD)和我十几年前熟悉的DDD(以下称为... 查看详情
领域驱动设计ddd的一些基础概念(代码片段)
...设计、战术设计DDD在微服务中解决的问题DDD的好处与局限领域领域、子域核心域、通用域、支撑域通用语言、限界上下文通用语言限界上下文实体、值对象实体值对象聚合和聚合根聚合聚合根如何设计聚合聚合的设计原则 &nb... 查看详情