DDD:订单管理 之 如何组织代码
2013-07-03 09:26
239 查看
背景
系统开发最难的是职责的合理分配,或者叫:“如何合理的组织代码”,今天说一个关于这方面问题的示例,希望大家多批评。示例背景
参考数据字典
![](http://images.cnitblog.com/blog/492619/201307/03085811-3ff94ee6022c4f61897d05a7f61e9f43.png)
需求
OrderCode必须唯一。Total = Sum(Subtotal)。
订单有三种状态:【未提交】、【待审核】和【已审核】,合理的状态迁移有:【未提交】----》【待审核】和【待审核】----》【已审核】,只有处于【未提交】状态的订单能修改。
订单和订单项中的状态必须合法,规则自己定义。
示例实现
项目结构
![](http://images.cnitblog.com/blog/492619/201307/03085925-1c850a625a3f45878348070cedc2962e.jpg)
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可以用来处理跨聚合通信,也可以用来处理长事务或离线事务。
重点介绍的领域层
![](http://images.cnitblog.com/blog/492619/201307/03091032-9b56eb6376494c39b8f9c2033ed4f208.jpg)
采用状态模式处理状态迁移。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Happy.Example.Domain.TestOrders { public interface ITestOrderState { void AddTestOrderDetail(TestOrderDetail testOrderDetail); void UpdateTestOrderDetail(Guid testOrderDetailId, decimal subtotal); void RemoveTestOrderDetail(Guid testOrderDetailId); void Commit(); void Verify(); string Status { get; } TestOrder TestOrder { set; } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Happy.Example.Domain.TestOrders { public partial class TestOrder { private abstract class TestOrderState : ITestOrderState { public virtual void AddTestOrderDetail(TestOrderDetail testOrderDetail) { this.ThrowInvalidOperationException(); } public virtual void UpdateTestOrderDetail(Guid testOrderDetailId, decimal subtotal) { this.ThrowInvalidOperationException(); } public virtual void RemoveTestOrderDetail(Guid testOrderDetailId) { this.ThrowInvalidOperationException(); } public virtual void Commit() { this.ThrowInvalidOperationException(); } public virtual void Verify() { this.ThrowInvalidOperationException(); } public abstract string Status { get; } public TestOrder TestOrder { protected get; set; } private void ThrowInvalidOperationException() { throw new InvalidOperationException(string.Format("处于【{0}】的订单不能执行此操作!", this.Status)); } } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Happy.Example.Domain.TestOrders { public partial class TestOrder { private sealed class UnCommitted : TestOrderState { internal static readonly string UnCommittedStatus = "未提交"; public override void AddTestOrderDetail(TestOrderDetail testOrderDetail) { this.TestOrder.DoAddTestOrderDetail(testOrderDetail); } public override void UpdateTestOrderDetail(Guid testOrderDetailId, decimal subtotal) { this.TestOrder.DoUpdateTestOrderDetail(testOrderDetailId, subtotal); } public override void RemoveTestOrderDetail(Guid testOrderDetailId) { this.TestOrder.DoRemoveTestOrderDetail(testOrderDetailId); } public override void Commit() { this.TestOrder.DoCommit(); } public override string Status { get { return UnCommittedStatus; } } } } }
采用封装集合手法处理Total的同步问题。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Happy.Domain; using Happy.Domain.Tree; using Happy.Infrastructure.ExtentionMethods; using Happy.Example.Events.TestOrders; namespace Happy.Example.Domain.TestOrders { public partial class TestOrder : AggregateRoot<Guid> { private ITestOrderState _orderState; protected TestOrder() { } public TestOrder(string orderCode, string customer) { orderCode.MustNotNullAndNotWhiteSpace("orderCode"); customer.MustNotNullAndNotWhiteSpace("customer"); this.Id = Guid.NewGuid(); this.OrderCode = orderCode; this.Customer = customer; this.OrderState = TestOrderStateFactory.CreateUnCommittedTestOrderState(this); this.TestOrderDetails = new List<TestOrderDetail>(); } public virtual System.String OrderCode { get; protected set; } public virtual System.String Customer { get; protected set; } public virtual System.Decimal Total { get; protected set; } public virtual System.String Status { get; protected set; } public virtual ICollection<TestOrderDetail> TestOrderDetails { get; protected set; } private ITestOrderState OrderState { get { if (_orderState == null) { _orderState = TestOrderStateFactory.Create(this, this.Status); } return _orderState; } set { _orderState = value; this.Status = value.Status; } } public void AddTestOrderDetail(TestOrderDetail testOrderDetail) { this.OrderState.AddTestOrderDetail(testOrderDetail); } public void UpdateTestOrderDetail(Guid testOrderDetailId, decimal subtotal) { this.OrderState.UpdateTestOrderDetail(testOrderDetailId, subtotal); } public void RemoveTestOrderDetail(Guid testOrderDetailId) { this.OrderState.RemoveTestOrderDetail(testOrderDetailId); } public void Commit() { this.OrderState.Commit(); } public void Verify() { this.OrderState.Verify(); } private void DoAddTestOrderDetail(TestOrderDetail testOrderDetail) { this.TestOrderDetails.Add(testOrderDetail); this.Total += testOrderDetail.Subtotal; } private void DoUpdateTestOrderDetail(Guid testOrderDetailId, decimal subtotal) { var testOrderDetail = this.TestOrderDetails.First(x => x.Id == testOrderDetailId); this.Total -= testOrderDetail.Subtotal; testOrderDetail.Subtotal = subtotal; this.Total += testOrderDetail.Subtotal; } private void DoRemoveTestOrderDetail(Guid testOrderDetailId) { var testOrderDetail = this.TestOrderDetails.First(x => x.Id == testOrderDetailId); this.TestOrderDetails.Remove(testOrderDetail); this.Total -= testOrderDetail.Subtotal; } private void DoCommit() { this.OrderState = TestOrderStateFactory.CreatePendingVerificationTestOrderState(this); } private void DoVerify() { this.OrderState = TestOrderStateFactory.CreateVerifiedTestOrderState(this); this.PublishEvent(new TestOrderVerified()); } } }
效果图
![](http://images.cnitblog.com/blog/492619/201307/03092408-44506be725f94ae3989fb8b8b17f6c3e.jpg)
背景
写这个简单的Demo过程,遇到了很多小的决策,这篇文章就看做一个开头吧,后边重点介绍每个决策点,在一篇文章中真的很难说完,喜欢看代码的朋友,先去https://happy.codeplex.com/下载。相关文章推荐
- DDD:订单管理 之 如何组织代码
- DDD:订单管理 之 如何组织代码
- 作为CTO,在技术成长和组织管理孰轻孰重,如何协同发展
- 高性能javascript读书笔记之 如何管理你的javascript代码
- Github上如何在组织中创建代码仓库,并如何授予该组织中某个小组权限?
- 如何组织大型JavaScript应用中的代码?(arvin 推荐)
- YbSoftwareFactory 代码生成插件【十一】:ASP.NET WebApi MVC下组织机构管理和菜单权限管理的实现
- 对于如何在编码过程中,我们如何管理代码的问题,下面是针对代码管理工具git的安装和使用
- AOSP是如何管理代码的
- 如何组织前端javascript代码
- 程序员如何管理自己的代码
- 读书笔记《编写高质量代码》-如何组织CSS
- 如何组织大型JavaScript应用中的代码
- 错误代码以及错误消息提示-如何更好地管理与维护消息资源
- 在阿里,我们如何管理代码分支?
- 如何使用IntelliJ IDEA的Favorites来管理项目中的常用代码
- 我和我的同伴是如何用具体的代码例子,说明源代码管理的基本操作
- [推荐]FACEBOOK是如何管理代码的
- 如何精细化管理代码质量