背景
系统开发最难的是职责的合理分配,或者叫:“如何合理的组织代码”,今天说一个关于这方面问题的示例,希望大家多批评。
示例背景
参考数据字典
需求
- OrderCode必须唯一。
- Total = Sum(Subtotal)。
- 订单有三种状态:【未提交】、【待审核】和【已审核】,合理的状态迁移有:【未提交】----》【待审核】和【待审核】----》【已审核】,只有处于【未提交】状态的订单能修改。
- 订单和订单项中的状态必须合法,规则自己定义。
示例实现
项目结构
- Application:应用层,负责领域逻辑的封装。主要角色:ApplicationService、CommandHandler。
- Boostrap:启动管理层,负责启动过程管理,如:注册Ioc、初始化配置。主要角色:BootstrapListener。
- Commands:命令层,是一个契约层。主要角色:Comamnd、DTO。
- Controllers:控制器层,边界层。主要角色:Controller。
- Domain:领域层,负责领域逻辑的组织。主要角色:Aggregate、Entity、ValueObject、Factory、DomainService、IRepository、IUnitOfWork。
- Events:事件层,是一个契约层,跨聚合流程可以采用。主要角色:Event。
- EventSubscribers:事件监听层。主要角色:EventSubscriber。
- Infrastructure:基础设施层。主要角色:Repository、QueryService、UnitOfWork。
- Query:查询层,为UI的查询提供服务,主要角色:QueryService。
项目整体采用简单的CQRS架构,Command端采用DDD组织,Query直接从数据库返回dynamic类型。Event可以用来处理跨聚合通信,也可以用来处理长事务或离线事务。
重点介绍的领域层
采用状态模式处理状态迁移。
- 1 using System;
- 2 using System.Collections.Generic;
- 3 using System.Linq;
- 4 using System.Text;
- 5 using System.Threading.Tasks;
- 6
- 7 namespace Happy.Example.Domain.TestOrders
- 8 {
- 9 public interface ITestOrderState
- 10 {
- 11 void AddTestOrderDetail(TestOrderDetail testOrderDetail);
- 12
- 13 void UpdateTestOrderDetail(Guid testOrderDetailId,decimal subtotal);
- 14
- 15 void RemoveTestOrderDetail(Guid testOrderDetailId);
- 16
- 17 void Commit();
- 18
- 19 void Verify();
- 20
- 21 string Status { get; }
- 22
- 23 TestOrder TestOrder { set; }
- 24 }
- 25 }
- 1 using System;
- 2 using System.Collections.Generic;
- 3 using System.Linq;
- 4 using System.Text;
- 5 using System.Threading.Tasks;
- 6
- 7 namespace Happy.Example.Domain.TestOrders
- 8 {
- 9 public partial class TestOrder
- 10 {
- 11 private abstract class TestOrderState : ITestOrderState
- 12 {
- 13 public virtual void AddTestOrderDetail(TestOrderDetail testOrderDetail)
- 14 {
- 15 this.ThrowInvalidOperationException();
- 16 }
- 17
- 18 public virtual void UpdateTestOrderDetail(Guid testOrderDetailId,decimal subtotal)
- 19 {
- 20 this.ThrowInvalidOperationException();
- 21 }
- 22
- 23 public virtual void RemoveTestOrderDetail(Guid testOrderDetailId)
- 24 {
- 25 this.ThrowInvalidOperationException();
- 26 }
- 27
- 28 public virtual void Commit()
- 29 {
- 30 this.ThrowInvalidOperationException();
- 31 }
- 32
- 33 public virtual void Verify()
- 34 {
- 35 this.ThrowInvalidOperationException();
- 36 }
- 37
- 38 public abstract string Status { get; }
- 39
- 40 public TestOrder TestOrder { protected get; set; }
- 41
- 42 private void ThrowInvalidOperationException()
- 43 {
- 44 throw new InvalidOperationException(string.Format("处于【{0}】的订单不能执行此操作!",this.Status));
- 45 }
- 46 }
- 47 }
- 48 }
- 1 using System;
- 2 using System.Collections.Generic;
- 3 using System.Linq;
- 4 using System.Text;
- 5 using System.Threading.Tasks;
- 6
- 7 namespace Happy.Example.Domain.TestOrders
- 8 {
- 9 public partial class TestOrder
- 10 {
- 11 private sealed class UnCommitted : TestOrderState
- 12 {
- 13 internal static readonly string UnCommittedStatus = "未提交";
- 14
- 15 public override void AddTestOrderDetail(TestOrderDetail testOrderDetail)
- 16 {
- 17 this.TestOrder.DoAddTestOrderDetail(testOrderDetail);
- 18 }
- 19
- 20 public override void UpdateTestOrderDetail(Guid testOrderDetailId,decimal subtotal)
- 21 {
- 22 this.TestOrder.DoUpdateTestOrderDetail(testOrderDetailId,subtotal);
- 23 }
- 24
- 25 public override void RemoveTestOrderDetail(Guid testOrderDetailId)
- 26 {
- 27 this.TestOrder.DoRemoveTestOrderDetail(testOrderDetailId);
- 28 }
- 29
- 30 public override void Commit()
- 31 {
- 32 this.TestOrder.DoCommit();
- 33 }
- 34
- 35 public override string Status
- 36 {
- 37 get { return UnCommittedStatus; }
- 38 }
- 39 }
- 40 }
- 41 }
采用封装集合手法处理Total的同步问题。
- 1 using System;
- 2 using System.Collections.Generic;
- 3 using System.Linq;
- 4 using System.Text;
- 5 using System.Threading.Tasks;
- 6
- 7 using Happy.Domain;
- 8 using Happy.Domain.Tree;
- 9 using Happy.Infrastructure.ExtentionMethods;
- 10 using Happy.Example.Events.TestOrders;
- 11
- 12 namespace Happy.Example.Domain.TestOrders
- 13 {
- 14 public partial class TestOrder : AggregateRoot<Guid>
- 15 {
- 16 private ITestOrderState _orderState;
- 17
- 18 protected TestOrder() { }
- 19
- 20 public TestOrder(string orderCode,string customer)
- 21 {
- 22 orderCode.MustNotNullAndNotWhiteSpace("orderCode");
- 23 customer.MustNotNullAndNotWhiteSpace("customer");
- 24
- 25 this.Id = Guid.NewGuid();
- 26 this.OrderCode = orderCode;
- 27 this.Customer = customer;
- 28 this.OrderState = TestOrderStateFactory.CreateUnCommittedTestOrderState(this);
- 29 this.TestOrderDetails = new List<TestOrderDetail>();
- 30 }
- 31
- 32 public virtual System.String OrderCode { get; protected set; }
- 33 public virtual System.String Customer { get; protected set; }
- 34 public virtual System.Decimal Total { get; protected set; }
- 35 public virtual System.String Status { get; protected set; }
- 36 public virtual ICollection<TestOrderDetail> TestOrderDetails { get; protected set; }
- 37
- 38 private ITestOrderState OrderState
- 39 {
- 40 get
- 41 {
- 42 if (_orderState == null)
- 43 {
- 44 _orderState = TestOrderStateFactory.Create(this,this.Status);
- 45 }
- 46
- 47 return _orderState;
- 48 }
- 49 set
- 50 {
- 51 _orderState = value;
- 52 this.Status = value.Status;
- 53 }
- 54 }
- 55
- 56 public void AddTestOrderDetail(TestOrderDetail testOrderDetail)
- 57 {
- 58 this.OrderState.AddTestOrderDetail(testOrderDetail);
- 59 }
- 60
- 61 public void UpdateTestOrderDetail(Guid testOrderDetailId,decimal subtotal)
- 62 {
- 63 this.OrderState.UpdateTestOrderDetail(testOrderDetailId,subtotal);
- 64 }
- 65
- 66 public void RemoveTestOrderDetail(Guid testOrderDetailId)
- 67 {
- 68 this.OrderState.RemoveTestOrderDetail(testOrderDetailId);
- 69 }
- 70
- 71 public void Commit()
- 72 {
- 73 this.OrderState.Commit();
- 74 }
- 75
- 76 public void Verify()
- 77 {
- 78 this.OrderState.Verify();
- 79 }
- 80
- 81 private void DoAddTestOrderDetail(TestOrderDetail testOrderDetail)
- 82 {
- 83 this.TestOrderDetails.Add(testOrderDetail);
- 84
- 85 this.Total += testOrderDetail.Subtotal;
- 86 }
- 87
- 88 private void DoUpdateTestOrderDetail(Guid testOrderDetailId,decimal subtotal)
- 89 {
- 90 var testOrderDetail = this.TestOrderDetails.First(x => x.Id == testOrderDetailId);
- 91
- 92 this.Total -= testOrderDetail.Subtotal;
- 93 testOrderDetail.Subtotal = subtotal;
- 94 this.Total += testOrderDetail.Subtotal;
- 95 }
- 96
- 97 private void DoRemoveTestOrderDetail(Guid testOrderDetailId)
- 98 {
- 99 var testOrderDetail = this.TestOrderDetails.First(x => x.Id == testOrderDetailId);
- 100
- 101 this.TestOrderDetails.Remove(testOrderDetail);
- 102 this.Total -= testOrderDetail.Subtotal;
- 103 }
- 104
- 105 private void DoCommit()
- 106 {
- 107 this.OrderState = TestOrderStateFactory.CreatePendingVerificationTestOrderState(this);
- 108 }
- 109
- 110 private void DoVerify()
- 111 {
- 112 this.OrderState = TestOrderStateFactory.CreateVerifiedTestOrderState(this);
- 113
- 114 this.PublishEvent(new TestOrderVerified());
- 115 }
- 116 }
- 117 }
效果图
背景
写这个简单的Demo过程,遇到了很多小的决策,这篇文章就看做一个开头吧,后边重点介绍每个决策点,在一篇文章中真的很难说完,喜欢看代码的朋友,先去https://happy.codeplex.com/下载。