1、编排业务逻辑的模式
1. 事务脚本模式TS(The Transaction Script pattern ) git
TS 鼓励你跳过任何的面向对象的设计,你直接到所需的用户操做的业务组件映射。专一于的业务用户能够经过表示层完成,并为每一个请求编写方法。这个方法被称之为事务脚本,此处事务一般是指想要进行商业交易,脚本是指系统中的一系列关系用户操做的系统操做。 github
TS历时多年仍然不过期的缘由只有一个:它基于推行可视化的业务逻辑设计,而可视化正上用户体验的核心。 web
在TS中,每一个用户操做都在上下文的物理事务边界中完成。不一样于数据访问层的数据执行脚本,TS的数据访问一般被封装在数据个组件中。按照设计,TS不包含使用面向对象设计。TS中的任务逻辑都是If,while,for等组成。 数据库
在实现中每一个TS都是一个单独的,可能表现为一个类的静态方法,能够在每一个类本身执行TS。当你这样作的时候,就已经完美演绎了命令模式。 设计模式
命令模式的主要设计思想是用一个对象来表示动做。命令对象封装一个动做和它全部的参数。典型的,命令对象公开的标准接口,以便调用方能够调用任何命令,而无需关注命令类的具体行为。如: api
public interface IApplicationCommand 架构
{ mvc
int Run(); app
} asp.net
public class BookHotelRoom : IApplicationCommand
{
Customer _guest;
DateTime _checkIn, _checkOut;
String _confirmationNumber;
// other internal members
public BookHotelRoom(Customer guest, DateTime checkIn, DateTime checkOut)
{
_guest = guest;
_checkIn = checkIn;
_checkOut = checkOut;
}
public String ConfirmationNumber
{
get { return _confirmationNumber; }
}
public int Run()
{
// Start the transaction
// Check room availability for requested stay
// Check customer information?(already guest, payment method, preferences)
// Calculate room rate
// Add a new record to the Bookings database table
// Generate the confirmation number
// Commit the transaction???????
// E-mail the customer
// Store the confirmation number to the local member _confirmationNumber
}
...
}
TS模式不对任务数据设计权限,不也会对数据作任何转换。它只用用于接收和发送数据,因此一般的作法是使用数据类(Dto)来传递数据。
2. 域模型模式(The Domain Model pattern)
域模型(DM)一般在DDD设计中使用,但也不局限于它。DM也是一个经常使用的设计模式,它让架构师侧重于系统的预期行为和运行时的数据流。
域模型是不一样于对象模型的一系列集合。域模型忠实于表示业务领域,特别是域内进程的数据流。
域模型是一系列表示业务领域的plain old classes。这些类是数据容器,能够表现为属性也能够是方法。The term POCO (Plain Old CLR Object)一般指这些域模型。
实际使用咱们不关注类自己,关注的是行为和事件。这些行为能帮助你理解在类上发生了什么。着眼于行为比着眼于类属性更有用,另外一方面看来,这就是Tell-Don't-Ask原则的体现。
有些时候域模型须要被持久化,而持久化不是域模型的职责,持久化将在基础结构层才能被实现。从应用程序看来域模型是业务逻辑数据库,不依赖任何接口,而持久化依赖接口,使用关系模型来持久化。域模型和关系模型一般要经过ORM工具来完成,如Microsoft's Entity Framework 和 NHibernate
3.反域模式ADM(The Anemic Domain Model (anti-)pattern)
域模式的核心于对象关联的行为,"行为先行"是违背域模型设计原则的,因此产生了另一种模式,反域模式(ADM)。
ADM域模型中,全部对象仍然都遵循命名约定的真实世界域实体、 实体之间的关系仍存在,和模型的总体结构紧密匹配的真实域空间。没有行为,只有属性的实体。
ADM模式建议不要在域对象里听任何逻辑。全部的逻辑都被放在逻辑领域的服务组件里。这此服务是域模型的消费者,而且有存储和持久化的功能。
■ Analysis leads to objects that closely model the domain space and are tailored to the real
needs
■ The strong domain perspective of things reduces the risk of misunderstandings between the
domain experts and the development team
■ Code is likely to become more readable and easily understandable
■ The fnal result has more chances to really be close to expectations.
2、将关注点从数据转移到任务
1. Asp.net Mvc中的任务
用户行为最终被实现为controller类中的一个方法调用。controller应该是被用来组织任务的首选。抽象的说,Contrller与WebForm中的postback事件没有区别。在这两个方法中,聪明的开发人员都会为了不web form中的完整的事件执行而选择使用Controller。
Responsibility-Driven Design (RDD)职责驱动设计由Rebecca Wirfs-Brock 和Alan McKean提出:Roles, Responsibilities, and Collaborations。RDD的本质是将系统分解成一系统可执行的Action动做,而后每一个action被映射到一个组件类。执行action被设计成组件类的职责。组件的解决依赖于它设定的职责。
Asp.net mvc 的controller是RDD模式的一个生动的实现,RDD协调者建议将action分组到一系统application services中,由controller 调用services来执行action返回view model
public ActionResult PlaceOrder(OrderInputModel orderInfo)
{
// Input data already mapped thanks to the model binding
// infrastructure of ASP.NET MVC
// Perform the task invoking a application service and
// get a view model back from application layer
var service = new OrderService();
var model = service.PlaceOrder();
// Invoke next view
return View(model);
}
controller负责展示层和应用层的协调调用,这种状况下若是须要不一样的展示层,能够用如下代码轻松实现:
var service = new OrderService();
var model = service.PlaceOrder();
// Adapt to the new view model
var newFrontendModel = someAdapter.NewViewModel(response);
应用层和展示层的链接点是controller,使用依赖倒置模式Ioc容器如microsoft unity来重写asp.net mvc的controller factory:
var factory = new UnityControllerFactory();
ControllerBuilder.Current.SetControllerFactory(factory);
UnityControllerFactory实现以下:
public class UnityControllerFactory : DefaultControllerFactory
{
public static IUnityContainer Container { get; private set; }
public UnityControllerFactory()
{
Container = new UnityContainer(); // Initialize the IoC
Container.LoadConfiguration(); // Configure it reading details from web.config
}
protected override IController GetControllerInstance(RequestContext context, Type type)
{
if (type == null) return null;
return Container.Resolve(type) as IController;
}
}
而后每一个controller类的默认构造函数要改为以下:
public class HomeController
{
private IHomeService _service;
public HomeController(IHomeService service)
{
_service = service;
}
...
}
You can use the Unity Mvc library available as a NuGet package For more information, see http://github com/feedbackhound/Unity Mvc5
一样的问题存在于数应用层和基础架构层的数据访问。使用相同的方法——依赖注入。在域模型场景中的数据访问逻辑容器一般被命名为repository
public class HomeService
{
private ISomeEntityRepository _someEntityRepo;
public HomeService(ISomeEntityRepository repo)
{
_someEntityRepo = repo;
}
...
}
有意思的是,你不须要在代码中修改初始化controller来设置factory,你只要确保仓储类型和接口都映射到Ioc容器里就能够。全部Ioc容器都提供配置和api两方式来映射类型。因此不须要再经过代码来配置,如今的IoC容器都提供了这种透明的方式来解决依赖关系。若是你通知IoC容器获取一个Controller类型,这个controller类型依赖一些Application Services类型,反过来,Services类型又依赖一个或多个仓储类型,全部这些代码都在一行代码里解决。
层与层之间的链接通讯建议使用依赖注入实现,但它不是惟一的解决方案。若是你有多个层在同一个进程空间内,比较简单的方法是直接而且本地实例化对象,不论他们在application layers、servces layers或是respositories layers. 缺点就是他们会在层之间产生高偶合。
// Tight coupling between the class OrderService
// and the class that contains this code
var service = new OrderService();
var model = service.PlaceOrder();
2. 编排域内的任务
一般、应用程序的逻辑层包含了任务编排、域逻辑和一些其它的东西。
域名服务一般包含业多个域实体操做的业务逻辑。大多状况下,域名服务是复杂、 多步的操做,都在域的边界内进行,并有多是基础设施层(infrastructure)。典型的例子是OrderProcessor, BestPriceFinder, 或GoldCustomerEvaluator这样的域服务。服务的命名以功能命名,而且可让领域专家和相关人员容易理解。
3、跨边界传递数据
表示层物理边界跨界,不管是进程跨界仍是物理计算机跨界都是一项很昂贵的操做。访问一个远程计算机的效率可能要比进程内通讯慢100倍。
1.层架构间的数据流
下图表示分层架构一个抽象的数据流。当执行一个命令执行时,数据流从用户接口以一个input model形势进入应用层。鉴于这个请求动做,应用程序层须要一系列用来作为input model的域模型实例。在一个领域分层系统中,持久性特指将转换成物理模型,妈实体关系模型(relational-data model),在返回结果时将data mode再转换为demain model。
理论上讲,一个分层的系统由上图的4种逻辑分离的model组成,但在有的状况下他们是一致的。data model一般与demain model在基础设施层是一致的。在Asp.net mvc应用程序中,input model与output model一般是同一个model。
在遵循域模型配置设计的架构中,域实体与数据紧密相关,推荐将域模型向表示层冒泡,而且在必要的时候将他们序列化来传输。
在一个解决方案中不多能让一个实知足整个系统的须要,毕竟不一样的层可能对数据要求不同。有些状况下,为了便捷使在全部层中都使用了demain model,有些状况下须要使用Dto类,总之、跟据你的须要。
Dto被设计为用来作物理层之间的数据载体。Dto类只有属性,没有行为。Dto原生能够被序列化,在远程组件调用中一般这样作。
典型的DTO使用如:显示和处理一个自定义订单。在处理订单时须要大量的数据,显示数据时只须要简单少许数据。DTO增长了层次的复杂度,可是减小了容器的复杂度。
Data model 与DTO之是能够经过AutoMapper适配器完成自动转换。
Mapper.CreateMap<YourSourceType, YourDtoType>();
Once a mapping is defned, you invoke it via the Map method:
var dto = Mapper.Map<YourDtoType>(sourceObject);