前言

领域驱动设计里有很多东西,我们可以应用在各种各样的开发模式里,所以接下来说的一些东西,我们可以部分使用。

说道领域驱动的领域,大家肯定就要开始说Bounded Context,聚合,聚合根,容易让大家搞糊涂。 我觉得先抛开这些概念,后面再来说如何设计聚合,先简单来说。

模型

过去,我们在多层设计里定义了很多Model, 数据库的Model(DB Entity), 然后为了不依赖数据库,我们有设计了业务的Domain Model, 同时我们又设计了ViewModel, 这样一般也没什么问题,职责也很清晰。但是有几个问题

  1. 我们要做很多的模型转换,转入转出。当然我们可以用AutoMapper来但是AutoMapper的性能实在难以恭维,大家可以在网上搜索AutoMapper performance.
  2. 领域模型成了一个单纯的DTO了。

领域模型

首先我们要看领域,就是我们尽量把业务聚合到一个领域里,比如我们要做一个功能,可以看到用户每一次的登录日志,那个这个登录日志其实就是属于用户这个领域里。

其次我们看模型,原来我们的模型都是只有属性,也就是贫血模型,贫血的意思就是没有行为,像木乃伊一样,但是实际上领域是我们要完成业务的最主要的地方,我们希望领域能够自制,也就是领域自己管理自己。

示例

比如有一个Employee, 他的状态有Active, Pending, DeActive, 业务上是Pending只能改为Active.

1
2
3
4
5
6
7
8
9
10
    public class Employee : Entity
{
public Name Name { get; set; } public EmployeeStatus EmployeeStatus { get; set; } }

如果是贫血的Employee模型,我们往往代码如下


public class EmployeeService : IEmployeeService
{
private readonly IUnitOfWorkFactory _unitOfWorkFactory;
private readonly IEmployeeRepository _employeeRepository; public EmployeeService(IUnitOfWorkFactory unitOfWorkFactory, IEmployeeRepository employeeRepository)
{
_unitOfWorkFactory = unitOfWorkFactory;
_employeeRepository = employeeRepository;
} public void ChangeStatus(EmployeeStatus status, Guid employeeId)
{
using (var unitOfWork = _unitOfWorkFactory.GetCurrentUnitOfWork())
{
var employee = _employeeRepository.GetById(employeeId);
employee.EmployeeStatus = status; unitOfWork.Commit();
}
}
}

但是上面的代码的问题就是领域没有自治,本来修改我的状态是我的事,你能不能修改,外面随意修改我的状态是很危险的,比如Pending状态只能改为Active状态。 所以如果不是贫血的模型,我们代码就会这样,让领域自己来管理

	public class Employee : Entity
{ public UserId UserId { get; private set; }
public EmployeeStatus EmployeeStatus { get; private set; } public void ChangeStatus(EmployeeStatus status)
{
if (this.EmployeeStatus == EmployeeStatus.Pending && status != EmployeeStatus.Active)
{
throw new Exception("Only can Active when status is pending");
} this.EmployeeStatus = status;
} } public class EmployeeService : IEmployeeService
{
private readonly IUnitOfWorkFactory _unitOfWorkFactory;
private readonly IEmployeeRepository _employeeRepository; public EmployeeService(IUnitOfWorkFactory unitOfWorkFactory, IEmployeeRepository employeeRepository)
{
_unitOfWorkFactory = unitOfWorkFactory;
_employeeRepository = employeeRepository;
} public void ChangeStatus(EmployeeStatus status, Guid employeeId)
{
using (var unitOfWork = _unitOfWorkFactory.GetCurrentUnitOfWork())
{
var employee = _employeeRepository.GetById(employeeId);
employee.ChangeStatus(status); unitOfWork.Commit();
}
}
}

因此可以看出,我们把业务代码尽量写在领域里让领域自治。

后记

其实领域驱动设计最难的就是设计领域(Domain), 也就是后面会说到的AggregateRoot 聚合,但是我想我们先让领域不再贫血,这样在传统的多层设计,数据驱动等架构都可以使用这种模式。

领域Model?的更多相关文章

  1. C#进阶系列——DDD领域驱动设计初探(二):仓储Repository(上)

    前言:上篇介绍了DDD设计Demo里面的聚合划分以及实体和聚合根的设计,这章继续来说说DDD里面最具争议的话题之一的仓储Repository,为什么Repository会有这么大的争议,博主认为主要原 ...

  2. C#进阶系列——DDD领域驱动设计初探(四):WCF搭建

    前言:前面三篇分享了下DDD里面的两个主要特性:聚合和仓储.领域层的搭建基本完成,当然还涉及到领域事件和领域服务的部分,后面再项目搭建的过程中慢慢引入,博主的思路是先将整个架构走通,然后一步一步来添加 ...

  3. C#进阶系列——DDD领域驱动设计初探(五):AutoMapper使用

    前言:前篇搭建了下WCF的代码,就提到了DTO的概念,对于为什么要有这么一个DTO的对象,上章可能对于这点不太详尽,在此不厌其烦再来提提它的作用: 从安全上面考虑,领域Model都带有领域业务,让Cl ...

  4. C#进阶系列——DDD领域驱动设计初探(七):Web层的搭建

    前言:好久没更新博客了,每天被该死的业务缠身,今天正好一个模块完成了,继续来完善我们的代码.之前的六篇完成了领域层.应用层.以及基础结构层的部分代码,这篇打算搭建下UI层的代码. DDD领域驱动设计初 ...

  5. DDD领域驱动设计仓储Repository

    DDD领域驱动设计初探(二):仓储Repository(上) 前言:上篇介绍了DDD设计Demo里面的聚合划分以及实体和聚合根的设计,这章继续来说说DDD里面最具争议的话题之一的仓储Repositor ...

  6. DDD领域驱动设计初探(七):Web层的搭建

    前言:好久没更新博客了,每天被该死的业务缠身,今天正好一个模块完成了,继续来完善我们的代码.之前的六篇完成了领域层.应用层.以及基础结构层的部分代码,这篇打算搭建下UI层的代码. DDD领域驱动设计初 ...

  7. DDD领域驱动设计初探(五):AutoMapper使用

    前言:前篇搭建了下WCF的代码,就提到了DTO的概念,对于为什么要有这么一个DTO的对象,上章可能对于这点不太详尽,在此不厌其烦再来提提它的作用: 从安全上面考虑,领域Model都带有领域业务,让Cl ...

  8. DDD领域驱动设计初探(四):WCF搭建

    前言:前面三篇分享了下DDD里面的两个主要特性:聚合和仓储.领域层的搭建基本完成,当然还涉及到领域事件和领域服务的部分,后面再项目搭建的过程中慢慢引入,博主的思路是先将整个架构走通,然后一步一步来添加 ...

  9. DDD领域驱动设计初探(二):仓储Repository(上)

    前言:上篇介绍了DDD设计Demo里面的聚合划分以及实体和聚合根的设计,这章继续来说说DDD里面最具争议的话题之一的仓储Repository,为什么Repository会有这么大的争议,博主认为主要原 ...

随机推荐

  1. DSO 优化代码中的 Schur Complement

    接上一篇博客<直接法光度误差导数推导>,DSO 代码中 CoarseInitializer::trackFrame 目的是优化两帧(ref frame 和 new frame)之间的相对状 ...

  2. Servlet笔记10--Session

    Web编程中的Session: 代码示例: package com.bjpowernode.javaweb.servlet; import java.io.IOException; import ja ...

  3. Expression Tree Build

    The structure of Expression Tree is a binary tree to evaluate certain expressions.All leaves of the ...

  4. C#上传图片(含有图片大小格式过滤以及改变像素安全存储)

    示例一: public JsonResult Upload(string parameter) { ]; try { //LogHelper.Info("文件长度:" + file ...

  5. like语句防止SQL注入

    mysql: select * from test where school_name like concat('%',${name},'%')   oracle: select * from tes ...

  6. laravel队列,事件简单使用方法

    A.队列的使用 1.队列配置文件存储在 config/queue.php 根据自己的情况进行配置 2..env文件 QUEUE_DRIVER=database(根据个人情况配置,redis等) 3.创 ...

  7. UE没法远程修改文件

    UE没法远程修改文件修改ftp和sftp修改方式都没有作用,考虑可能是防火墙的作用,关闭防火墙可以.于是在控制面板->防火墙->修改策略中将UE的公用网络打开.

  8. ubuntu16。04LST配置nfs实现服务器和客户端共享文件

    NFS(network file system)网络文件系统可以实现不同主机与操作系统之间通过网络进行资源共享,此时一台PC充当服务器,若干台PC充当那客户端,具体如何配置请跟随我的步骤来做 1 下载 ...

  9. Coursera台大机器学习技法课程笔记13-Deep Learning

    深度学习面临的问题和现在解决的办法: 简要来说,分两步使用DL:初始化时一层一层的选择权重,而后再进行训练: 那么怎么做pre-training,即怎么选择权重呢?好的权重能够不改变原有资料的信息,即 ...

  10. 【LOJ】#2178. 「BJOI2017」机动训练

    题解 遇见平方和就转有序对呗 dp类似从很多点出发每次走一步的转移方式 然后我too naive的,枚举路径长度来决定更新次数,愉快TLE 改成记搜就过了 代码 #include <bits/s ...