前边我们构建了个数据访问层,功能虽然简单,但是基本够用了。传送门:项目架构开发:数据访问层
这次我们构建业务逻辑层
业务逻辑是一个项目、产品的核心,也是现实世界某种工作流程在代码层面的体现。
所以,业务逻辑的合理组织构造,或更真实地反映现实业务操作,对项目的成功与否非常重要
现在业界对业务逻辑层的开发,一般会参考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
7 {
8 }
9 }
LoginUser实体实际上就是与数据表对于的PO,只有属性,没有任何逻辑,严格来说都不算是一个模型
IRepository是定义模型具有哪些方法,这里直接继承前边数据访问层的基础接口,这里再贴一次
IRepository.cs
1 public interface IRepository
2 {
3 void Add(T entity);
4 void AddBatch(IEnumerable
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
15 IEnumerable
16 IEnumerable
17 Tuple
18 long Count(Expression
19 }
可以看到,模型只有一些简单的CURD操作,而且访问仅限于自己领域内,如果要自定义功能,要在ILoginUserRepository中扩充
我们再来看看LoginUser模型的一个具体实现
LoginUserRepository.cs
1 public class LoginUserRepository : BaseRepository
2 {
3 public LoginUserRepository(IUnitOfWork
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
13 private ILoginUserRepository repository;
14
15 public LoginUserApplication()
16 {
17 this.unitOfWork = new UnitOfWork
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, 貌似就流行这么搞;这让我很诧异。但是这种模式也是有很大好处的:足够简单、适合分层并行开发
在项目初期的速度大大高于充血模型的速度;业务充血模型需要具备的业务领域能力太高,单单建模就花很多时间,
在现在这种互联网公司,多数老板不让你这么搞,都是草草了事。反正我完成了的任务,管他后边洪水滔天,所以搞的大家都很浮躁
发表评论