前言

基于 DDD 传统分层架构实现。 项目 github地址:https://github.com/WuMortal/DDDSample

这个分层架构是工作中项目正在使用的分层架构,使用了一段时间发现受益匪浅,所以整理好我对该分层架构的一些理解分享给大家,我对于该分层架构还处于学习阶段理解有误的地方请指出。本次会以一个案例来说明各个分层的作用以及他们之间的调用关系,还有本次的重点不在于DDD,因为这个我还未能完全理解,当然避免不了中间会涉及DDD的一些概念。

DDD 简单介绍

DDD 什么?为什么使用 DDD

关于这个问题有兴趣的可以自行百度,我相信网络上已经有大量的文章来说明这几个问题。我目前的理解是“业务”,是为了应对现在复杂和多变的业务,是一种开发理念。

这里我就以一个小故事描述吧,有一天你接到任务要实现一个修改用户的功能,非常简单。使用传统三层架构我们会怎么写?

  1. 先在 DAL 层添加 UserDAL 然后实现一个 Update(UserEntity user) 方法

  2. 接着在 BLL 中添加一个 UserBLL 在实现一个 Update(string email,string pwd ...) 方法。

  3. UI 层在调用,OK 完成任务下班回家。

接着你接到一个新的需求就是:需要增加用户修改信息的记录。

你立马在 BLLUpdate 的方法里增加的用户修改信息的操作记录,完成需求。

过了一段时间又来了一个需求:用户改了信息需要通知到管理员,并且用户每天只能修改 3 次信息。

好了之后又经历了几波需求,你的代码也在不断的增加和变化,有一天你接收新的项目或者离开了,那么接收你项目的人完全不清楚这里的业务情况。因为 Update 方法并没有直接的反应出里的业务情况,代码目的不明确。代码变得难以维护。

那么在 DDD 里这些应该怎么做呢?

  1. 首先在方法的命名上做出更改既然业务是修改信息那么命名应该是 Modify(string email,string pwd ...)

  2. 将用户修改信息的记录代码放在 DomainService(领域服务) 中,当然这里的类、方法命名要直接的反应出业务情况,如:RecordUserModifyDomainService

  3. 对应的通知管理员的代码也应该放入 DomainService 中,DomainService 应该尽量简单一般只做一件事情。

分层架构图

下面是关于 DDD 分层的一些描述,摘抄至之前看过的一片文章。

  • Presentation 为表示层,负责向用户显示信息和解释用户命令。这里指的用户可以是另一个计算机系统,不一定是使用用户界面的人。

  • Application 为应用层,定义软件要完成的任务,并且指挥表达领域概念的对象来解决问题。这一层所负责的工作对业务来说意义重大,也是与其它系统的应用层进行交互的必要渠道。应用层要尽量简单,不包含业务规则或者知识,而只为下一层中的领域对象协调任务,分配工作,使它们互相协作。它没有反映业务情况的状态,但是却可以具有另外一种状态,为用户或程序显示某个任务的进度。

  • Domain 为领域层(或模型层),负责表达业务概念,业务状态信息以及业务规则。尽管保存业务状态的技术细节是由基础设施层实现的,但是反映业务情况的状态是由本层控制并且使用的。领域层是业务软件的核心,领域模型位于这一层。

  • Infrastructure 层为基础实施层,向其他层提供通用的技术能力:为应用层传递消息,为领域层提供持久化机制,为用户界面层绘制屏幕组件,等等。基础设施层还能够通过架构框架来支持四个层次间的交互模式。

说明

如上图每个层中其实对应着具体的项,下面将对每个项进行说明。

  1. Domain 层分为:DomainDomainServiceIDomainService

    • 首先 Domain 中包含有 EntityIRepositoryEntity 是你的实体一般对于数据库表但是在某些情况下你也可以冗余一些字段。IRepository 仓储的方法的定义,该层不会有具体的实现。
    • DomainServiceIDomainServiceIDomainService 只是负责表达业务的概念,DomainService 里才是具体业务逻辑代码。在这一层的代码命名上需要注意,我们的命名一般要能直接描述出该代码业务的功能。这里可以参考 DDD 的几个概念:通用语言、领域。
  2. Infrastructure 层分为:RepositoryCrossCutting

    • Repository 里面就是 DomainIRepository 的具体实现。项目中 RepositoryExtensions.cs 是一个扩展类,将所有的仓储注入容器中,方便我们在项目中使用 DI(依赖注入)。
    • CrossCutting 主要是提供一些各个层通用的东西,如一些枚举、扩展方法、工具类等等。
  3. Application 层分为:ApplicationApplicationContract

    • ApplicationContract 里主要包含 DTOViewModelIXXXServiceDTO 是数据传输对象,主要负责给展现层提供展示数据,DTO 里应该只有值类型存在,当然根据具体情况也可存在其他的 DTOViewModel 用于展现层传入的模型,简单的说 DTO 输出,ViewModel 输入。IXXXService 就是应用层的方法定义。
    • Application 里面主要是用于 实现 ApplicationContract 里的 IXXXService,还有 EntityDTO 的映射也属于该层的工作。ApplicationExtensions.cs 扩展方法是用于实现 DI
  4. Presentation 层里目前只有一个 WebAPI。展现层的代码一般有:对传入模型的校验。

案例

本次以一个用户注册的流程为案例,来简单说明如何使用该分层架构进行项目开发。

  1. 首先在 Domain 中建一个 UserEntity,有 Id、Mobile、Name、Age、RegisterDateTime 属性。接着建立 IUserRepository,编写需要定义的方法,这里我定义了一个 GetByMobile(string mobile) 方法。
  [Table(Name = "User")]
public class UserEntity
{
[Column(IsIdentity = true)] public Guid Id { get; set; } public string Mobile { get; set; } public string Name { get; set; } public int Age { get; set; } public DateTime RegisterDateTime { get; set; } = DateTime.Now;
} public interface IUserRepository : IBasicRepository<UserEntity, Guid> { Task GetByMobileAsync(string mobile); }

IBasicRepository 是使用了 FreeSql,你们可以自己实现。

  1. 然后在 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
}
  1. 仓储基本好了后就是 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; }
}
  1. 添加好 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
}
  1. UserServcie 是对应展现层的控制器 UserController ---> IUserService。

  2. 最后展现层的 WebAPI 只需要注入 IUserService,就可以开心的使用了。

[HttpPost] public async Task Post()
{
var second = DateTime.Now.Second.ToString(""); bool isSuccess = await _userService.Register("Wigor", $"188888888{second}", ); return Ok(isSuccess);
}

就这样这个简单的案例就完成了,你可以参考着上面 说明 对比着去看看,当然这里有一些东西并没有体现,如 DomainServie,如果按照 DDD 来说还有 值对象、聚合、通用语言……,对于「通用语言」的话其实上面的小故事就体现出了一点。

结语

就 DDD 而言我这里还有很多东西都没有交代,今后有时间的话会慢慢的写出来。还有我也是在学习 DDD 所以有错的地方请指出,望多多包涵。

在使用这套分层架构的时候碰到了许多问题,这里还要感谢老大的指导,为我解答疑问。

最后附上《实现领域驱动设计》中的一句话:

我认为不管使用什么技术,我们的目的都是提供业务价值。

DDD「领域驱动设计」分层架构初探的更多相关文章

  1. 我的“第一次”,就这样没了:DDD(领域驱动设计)理论结合实践

    写在前面 插一句:本人超爱落网-<平凡的世界>这一期,分享给大家. 阅读目录: 关于DDD 前期分析 框架搭建 代码实现 开源-发布 后记 第一次听你,清风吹送,田野短笛:第一次看你,半弯 ...

  2. 一缕阳光:DDD(领域驱动设计)应对具体业务场景,如何聚焦 Domain Model(领域模型)?

    写在前面 阅读目录: 问题根源是什么? <领域驱动设计-软件核心复杂性应对之道>分层概念 Repository(仓储)职责所在? Domain Model(领域模型)重新设计 Domain ...

  3. DDD(领域驱动设计)应对具体业务场景,如何聚焦 Domain Model(领域模型)?

    DDD(领域驱动设计)应对具体业务场景,如何聚焦 Domain Model(领域模型)? 阅读目录: 问题根源是什么? <领域驱动设计-软件核心复杂性应对之道>分层概念 Repositor ...

  4. DDD(领域驱动设计)理论结合实践

    DDD(领域驱动设计)理论结合实践   写在前面 插一句:本人超爱落网-<平凡的世界>这一期,分享给大家. 阅读目录: 关于DDD 前期分析 框架搭建 代码实现 开源-发布 后记 第一次听 ...

  5. 【DDD】领域驱动设计实践 —— UI层实现

    前面几篇blog主要介绍了DDD落地架构及业务建模战术,后续几篇blog会在此基础上,讲解具体的架构实现,通过完整代码demo的形式,更好地将DDD的落地方案呈现出来.本文是架构实现讲解的第一篇,主要 ...

  6. 如何使用ABP进行软件开发(2) 领域驱动设计和三层架构的对比

    简述 上一篇简述了ABP框架中的一些基础理论,包括ABP前后端项目的分层结构,以及后端项目中涉及到的知识点,例如DTO,应用服务层,整洁架构,领域对象(如实体,聚合,值对象)等. 笔者也曾经提到,AB ...

  7. DDD(领域驱动设计)应对具体业务场景,Domain Model(领域模型)到底如何设计?

    DDD(领域驱动设计)应对具体业务场景,Domain Model(领域模型)到底如何设计? 写在前面 阅读目录: 迷雾森林 找回自我 开源地址 后记 毫无疑问,领域驱动设计的核心是领域模型,领域模型的 ...

  8. 【DDD】领域驱动设计实践 —— 架构风格及架构实例

    概述 DDD为复杂软件的设计提供了指导思想,其将易发生变化的业务核心域放置在限定上下文中,在确保核心域一致性和内聚性的基础上,DDD可以被多种语言和多种技术框架实现,具体的框架实现需要根据实际的业务场 ...

  9. DDD(领域驱动设计)总结

    基本概念: 领域驱动设计(简称 ddd)概念来源于2004年著名建模专家eric evans发表的他最具影响力的书籍:<domain-driven design –tackling comple ...

随机推荐

  1. 【.NET Core】ASP.NET Core之IdentityServer4(1):快速入门

    [.NET Core]ASP.NET Core之IdentityServer4 本文中的IdentityServer4基于上节的jenkins 进行docker自动化部署. 使用了MariaDB,EF ...

  2. vfd with stm8

    2018-01-14 22:50:26 之前写了pt6311的驱动,要做时钟考虑使用stm8做主控,于是乎将之前的驱动移植到stm8上. 顺带熟悉了stm8的操作2333. 上源码: #ifndef ...

  3. MFC学习问题总结

    1.学习MFC添加位图,无法获取其ID 1).点击视图->其他窗口->资源视图,你会发现“无法在此窗口显示”,找到resource.h文件,关闭即可重新走一遍上面的过程就会发现可以打开了. ...

  4. Codeforces 623D [Amazing概率题]

    很有趣的一道题吖! 做法:贪心+迭代 Sigma(i*(pr[i]-pr[i-1])))=n-sigma(pr[i]), 所以我们贪心地是pr[i]尽可能大. 也就是让pr[i]/pr[i-1]尽可能 ...

  5. 基于puppeteer模拟登录抓取页面

    关于热图 在网站分析行业中,网站热图能够很好的反应用户在网站的操作行为,具体分析用户的喜好,对网站进行针对性的优化,一个热图的例子(来源于ptengine) 上图中能很清晰的看到用户关注点在那,我们不 ...

  6. MySQL创建全文索引

    使用索引时数据库性能优化的必备技能之一.在MySql数据库中,有四种索引:聚焦索引(主键索引).普通索引.唯一索引以及我们这里将要介绍的全文索引(FUNLLTEXT INDEX). 全文索引(也称全文 ...

  7. SOFA 源码分析 — 自动故障剔除

    前言 集群中通常一个服务有多个服务提供者.其中部分服务提供者可能由于网络,配置,长时间 fullgc ,线程池满,硬件故障等导致长连接还存活但是程序已经无法正常响应.单机故障剔除功能会将这部分异常的服 ...

  8. Redis案例——商品秒杀,购物车

    秒杀案例: <?php header("content-type:text/html;charset=utf-8"); $redis = new redis(); $resu ...

  9. Maven手动添加jar包

    有的jar在Maven中找不到则需要手动添加(如ojdbc14.jar) 方法如下: 一.将你要添加的jar包放到指定目录(在该目录下打开命令窗口) 二.输入指令:mvn install:instal ...

  10. java数据库基本操作(sqlserver 2000为例)

    一.环境搭建 1.下载对应数据库连接驱动包并引入. 2.如果在web中调用必须在tomcat中也放入对应的驱动包. 3.在jre的lib\ext中也加入对应的驱动包. 二.连接数据库 public s ...