zoukankan      html  css  js  c++  java
  • 项目架构开发:业务逻辑层之领域驱动失血模型

    前边我们构建了个数据访问层,功能虽然简单,但是基本够用了。传送门:项目架构开发:数据访问层

    这次我们构建业务逻辑层

    业务逻辑是一个项目、产品的核心,也是现实世界某种工作流程在代码层面的体现。

    所以,业务逻辑的合理组织构造,或更真实地反映现实业务操作,对项目的成功与否非常重要

    现在业界对业务逻辑层的开发,一般会参考Martin Fowler大师提出来的针对业务层开发的四种模式

    分别是面向过程的事务脚本、表模块模式,面向对象的活动记录与领域开发模式

    我们要做的就是领域驱动开发模式,注意标题中的“失血模式”,我们的业务领域模型不是贫血与充血,而是失血;

    这意味着领域模型只有get;set;,模型的所有行为都在领域模型之外,我们的领域逻辑在IRepository组件中、应用逻辑在Application组件中

    1、领域模型

    LoginUser.cs 

     1 using System;
     2 
     3 namespace Business.ReverseDDD.Model
     4 {
     5     public class LoginUser
     6     {
     7         public Guid? Id { get; set; }
     8         public string LoginName { get; set; }
     9         public string Password { get; set; }
    10         public int? IsEnabled { get; set; }
    11         public DateTime? CreateTime { get; set; }
    12     }
    13 }

    ILoginUserRepository.cs

    1 using Business.ReverseDDD.Model;
    2 using Infrastructure.Interface;
    3 
    4 namespace Business.ReverseDDD.IRepository
    5 {
    6     public interface ILoginUserRepository : IRepository<LoginUser>
    7     {
    8     }
    9 }

    LoginUser实体实际上就是与数据表对于的PO,只有属性,没有任何逻辑,严格来说都不算是一个模型

    IRepository是定义模型具有哪些方法,这里直接继承前边数据访问层的基础接口,这里再贴一次

    IRepository.cs

     1 public interface IRepository<T> where T : class
     2     {
     3         void Add(T entity);
     4         void AddBatch(IEnumerable<T> entitys);
     5         void Update(T entity);
     6         void Delete(T entity);
     7         void Delete(string Id);
     8         void Delete(int Id);
     9         void Delete(Guid Id);
    10         T Get(string Id);
    11         T Get(Guid Id);
    12         T Get(int Id);
    13         T Get(T entity);
    14         T Get(Expression<Func<T, bool>> func);
    15         IEnumerable<T> GetAll();
    16         IEnumerable<T> GetList(Expression<Func<T, bool>> where = null, Expression<Func<T, bool>> order = null);
    17         Tuple<int, IEnumerable<T>> GetPage(Page page, Expression<Func<T, bool>> where = null, Expression<Func<T, bool>> order = null);
    18         long Count(Expression<Func<T, bool>> where = null);
    19     }

    可以看到,模型只有一些简单的CURD操作,而且访问仅限于自己领域内,如果要自定义功能,要在ILoginUserRepository中扩充

    我们再来看看LoginUser模型的一个具体实现

    LoginUserRepository.cs

    1     public class LoginUserRepository : BaseRepository<LoginUser>, ILoginUserRepository
    2     {
    3         public LoginUserRepository(IUnitOfWork<LoginUser> unitOfWork)
    4             : base(unitOfWork)
    5         {
    6 
    7         }
    8     }

    这里也是继承前边数据访问层的BaseRepository.cs类,这个类有点长,就不贴了,可以到前边看看详细的

    好了,现在我们的领域逻辑做好了,模型的CURD都已经委托给了基础设施层来做。现在可以开始对构建应用逻辑了

    2、构建应用逻辑

    LoginUserApplication.cs

     1 using Business.DTO.Request;
     2 using Business.ReverseDDD.IRepository;
     3 using Business.ReverseDDD.Model;
     4 using Business.ReverseDDD.Repository;
     5 using Infrastructure.Data.UnitOfWork;
     6 using Infrastructure.Interface;
     7 
     8 namespace Business.ReverseDDD.Application
     9 {
    10     public class LoginUserApplication
    11     {
    12         private IUnitOfWork<LoginUser> unitOfWork;
    13         private ILoginUserRepository repository;
    14 
    15         public LoginUserApplication()
    16         {
    17             this.unitOfWork = new UnitOfWork<LoginUser>();
    18             this.repository = new LoginUserRepository(this.unitOfWork);
    19         }
    20 
    21         public bool Login(LoginRequest entity)
    22         {
    23             var user = this.repository.Get(w => w.LoginName == entity.LoginName);
    24 
    25             if (user == null) return false;
    26             if (user.Password != entity.Password) return false;
    27 
    28             return true;
    29         }
    30     }
    31 }

    这里可以用IOC的方式解耦UnitOfWork

    应用逻辑层Evans的定义是这一层保持简单,并不需要了解业务规则,将业务规则直接传给下一层领域模型完成就OK了

    但是我们是失血模型,模型内没有领域逻辑的实现,所以这一层必须实现所有业务逻辑

    还有请注意LoginRequest这个参数类,这个类是DTO对象,用于展示层与业务逻辑层传输数据用的,目的当然是为了解耦了

    3、DTO

    LoginUser.cs

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 
     6 namespace Business.DTO.Request
     7 {
     8     public class LoginRequest
     9     {
    10         public string LoginName { get; set; }
    11         public string Password { get; set; }
    12     }
    13 }

    非常简单,只是一些传输参数的实体类,我们再新建一个测试项目看看实际运行效果

    4、UnitTest

    LoginUserApplicationTest.cs

     1     public class LoginUserApplicationTest
     2     {
     3         public LoginUserApplicationTest()
     4         { }
     5 
     6         [TestMethod]
     7         public void Login()
     8         {
     9             var loginUserApplication = new LoginUserApplication();
    10             var flag = loginUserApplication.Login(new LoginRequest()
    11             {
    12                 LoginName = "lanxiaoke-333",
    13                 Password = "mima1987"
    14             });
    15 
    16             Assert.AreEqual(true, flag);
    17         }
    18     }

    表数据 

     运行结果

     好了,貌似没什么问题;看一下完整方案

    最后总结: 

    我最近正在学习充血模型的DDD,也知道这篇所谓的“领域驱动”其实跟真正的领域开发有很大的差距,甚至不是真正的OO,只是事务脚本的变种而已

    从网上搜了一些DDD的demo, 貌似就流行这么搞;这让我很诧异。但是这种模式也是有很大好处的:足够简单、适合分层并行开发

    在项目初期的速度大大高于充血模型的速度;业务充血模型需要具备的业务领域能力太高,单单建模就花很多时间,

    在现在这种互联网公司,多数老板不让你这么搞,都是草草了事。反正我完成了的任务,管他后边洪水滔天,所以搞的大家都很浮躁

  • 相关阅读:
    The Collections Module内建collections集合模块
    生成器接受和返还功能在执行过程中的详解以及生成器实现协同
    写python中的装饰器
    windows下载Mysql-python
    分别用单线程和多线程运行斐波那契、阶乘和累加和
    TCP客户端和服务器间传输数据遇到的TypeError: a bytes-like object is required, not 'str'问题
    python的property属性
    python的伪私有属性
    使用栈实现中缀表达式转为后缀表达式和后缀表达式的求解
    公众帐号如何向用户发送emoji表情(php版,附emoji编码表)
  • 原文地址:https://www.cnblogs.com/lanxiaoke/p/6505351.html
Copyright © 2011-2022 走看看