基于 DDD 传统分层架构实现。 项目 github地址:https://github.com/WuMortal/DDDSamplegit
这个分层架构是工做中项目正在使用的分层架构,使用了一段时间发现受益不浅,因此整理好我对该分层架构的一些理解分享给你们,我对于该分层架构还处于学习阶段理解有误的地方请指出。本次会以一个案例来讲明各个分层的做用以及他们之间的调用关系,还有本次的重点不在于DDD
,由于这个我还未能彻底理解,固然避免不了中间会涉及DDD
的一些概念。github
DDD
什么?为何使用 DDD
?数据库
关于这个问题有兴趣的能够自行百度,我相信网络上已经有大量的文章来讲明这几个问题。我目前的理解是“业务”,是为了应对如今复杂和多变的业务,是一种开发理念。网络
这里我就以一个小故事描述吧,有一天你接到任务要实现一个修改用户的功能,很是简单。使用传统三层架构咱们会怎么写?架构
先在 DAL
层添加 UserDAL
而后实现一个 Update(UserEntity user)
方法框架
接着在 BLL
中添加一个 UserBLL
在实现一个 Update(string email,string pwd ...)
方法。async
UI
层在调用,OK 完成任务下班回家。工具
接着你接到一个新的需求就是:须要增长用户修改信息的记录。学习
你立马在 BLL
的 Update
的方法里增长的用户修改信息的操做记录,完成需求。ui
过了一段时间又来了一个需求:用户改了信息须要通知到管理员,而且用户天天只能修改 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,大家能够本身实现。
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; } }
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", $"188888888{second}", 22); return Ok(isSuccess); }
就这样这个简单的案例就完成了,你能够参考着上面 说明 对比着去看看,固然这里有一些东西并无体现,如 DomainServie,若是按照 DDD 来讲还有 值对象、聚合、通用语言……,对于「通用语言」的话其实上面的小故事就体现出了一点。
就 DDD 而言我这里还有不少东西都没有交代,从此有时间的话会慢慢的写出来。还有我也是在学习 DDD 因此有错的地方请指出,望多多包涵。
在使用这套分层架构的时候碰到了许多问题,这里还要感谢老大的指导,为我解答疑问。
最后附上《实现领域驱动设计》中的一句话:
我认为无论使用什么技术,咱们的目的都是提供业务价值。