65.9K
CodeProject 正在变化。 阅读更多。
Home

将您的想法付诸实践

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.50/5 (2投票s)

2013年2月28日

CPOL

5分钟阅读

viewsIcon

15724

工作单元和领域设计。

引言

诸如聚合根、接口标记、实体、值对象和工作单元之类的模式强制执行了一种能够赋予你的应用程序强大功能的纪律。

从根部开始,向前推进

应用程序域是你业务解决方案理念所在之处。在你的域的根部,也就是一切汇集的地方,聚合处,应该是你的聚合根。聚合根充当标记。它是一种接口标记模式,用于标记域中的类为实体。

聚合根是一个接口,是你域中实体实现的契约。它们通过实现该接口来签署。通过创建实体基类,它可以继承该接口,而你所有的其他实体都可以继承该基类。使用接口定义基类意味着这些类中的每一个都将被标记为聚合根作为实体。

public interface IAggregateRoot
{
}

public abstract class EntityBase<TId> : IAggregateRoot
{
}

public class Order : EntityBase<Guid>
{
    public Guid Id;
}

实体是你的应用程序处理的对象。你的应用程序将它们作为个体来处理,它们具有身份。订单录入处理订单。订单录入系统将订单作为实体。每个订单对系统来说都单独很重要,需要有标识符。Order类应该继承AggregateRoot以将其定义为Entity。

上面例子中的基类使用了泛型,因此你可以创建使用整数或GUID的类。GUID是更好的选择,因为它们可以在域内创建。为了确保整数标识符的唯一性,它需要从数据存储中检索,这使得它依赖于域之外的东西。

不是我们解决方案的焦点,我们不单独处理的事物是值对象。在订单录入系统中,送货方式会是一个值对象。送货方式用于帮助描述实体,订单。它不会被单独处理。它是一个设置,一个属性,一个值。

public abstract class ValueObjectBase
{
}

public class ShippingMode  : ValueObjectBase
{
}

实体和值对象由它们的使用方式定义。送货方式在订单录入系统中将是一个值对象,但在管理系统中它可能是一个实体。这取决于对象的使用方式,是否被单独处理。在处理实体时,我们经常执行创建、读取、更新和删除(CRUD)操作。读取不会改变数据存储,但CUD操作会。

大多数时候,数据存储的更改都很简单。更新一个实体,比如订单,通过更改值对象的值,比如送货方式,只需要一个命令就可以完成。但对于更复杂的动作,比如下单,可能需要向数据存储发出许多命令。金钱可能易手,库存可能已更新。所有类型的事情都可能需要发生才能完成一件事。如果所有这些都不能正确发生,它们可能会导致你的数据混乱,你的客户得不到服务,并且可能会挂起你的程序。

解决方案是一个称为工作单元(Unit of Work)的概念或模式,它使用一种称为事务(transaction)的技术。通过事务,对数据存储的更改可以作为一个整体进行,如果任何一项失败,所有更改都会被回滚到尝试更改之前。你的数据提供者知道如何处理事务。通过引用System.Transactions,你的域将知道如何为你的提供者提供事务。工作单元模式就是告诉它的方式。

仓库(repository)是存放东西的地方。我们想要创建一个地方来持有和存储CUD操作,以便我们以后可以捆绑它们并将它们交给数据存储作为事务执行。由于创建与更新不同,而删除也是不同的,最好将它们存储在工作单元仓库的3个不同的部分。持有某物被称为持久化。工作单元方法经常使用"persist"一词,但这个例子使用了与本解释相符的名称。

public interface IUnitOfWorkRepository
{
    void HoldOnToCreationOf(IAggregateRoot entity);
    void HoldOnToUpdateOf(IAggregateRoot entity);
    void HoldOnToDeleteOf(IAggregateRoot entity);
}

这些方法中的每一个都要求提供聚合根的签名。应用程序将要创建、更新或删除的每一项都将是一个实体。为了强制执行该规则,将要存储用于CUD操作的仓库将只接受IAggregateRoot的实例。

 该接口作为规则的标记。工作单元仓库有一个规则,一个契约,即它只处理域实体,实现聚合根的类。在持有所有这些已更改的实体之后,需要将它们捆绑起来,然后找到一种提交的方式来完成更改。我们的域是一个抽象,只是一个模型。我们不想用现实世界来破坏它。在域中,我们不关心现实世界中事情是如何完成的,只关心我们需要如何告诉它来完成。

public interface IUnitOfWork
{
    void AddtoUpdatedBundle(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository);
    void AddtoCreatedBundle(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository);
    void AddtoDeletedBundle(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository);
    void Commit();
}

我们的域表示,如果我们想将数据存储更改预先打包成一个大集合,打包成一个事务,我们需要将每种更改类型的待处理更改以及相应的实体捆绑在一起。我们还指定了提交更改的命令。这个UnitOfWork类的实现使用字典项作为捆绑包,并创建一个作用域来保存事务的内容。

public class UnitOfWork : IUnitOfWork
{
    private Dictionary<IAggregateRoot, IUnitOfWorkRepository> addedEntities;
    private Dictionary<IAggregateRoot, IUnitOfWorkRepository> changedEntities;
    private Dictionary<IAggregateRoot, IUnitOfWorkRepository> deletedEntities;

    private TransactionScope scope;

    public UnitOfWork()
    {
        addedEntities = new Dictionary<IAggregateRoot, IUnitOfWorkRepository>();
        changedEntities = new Dictionary<IAggregateRoot, IUnitOfWorkRepository>();
        deletedEntities = new Dictionary<IAggregateRoot, IUnitOfWorkRepository>();
    }

    public void AddtoUpdatedBundle(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository)
    {
        if (!changedEntities.ContainsKey(entity)) {
            changedEntities.Add(entity, unitofWorkRepository);
        }
    }

    public void AddtoCreatedBundle(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository)
    {
        if (!addedEntities.ContainsKey(entity)) {
            addedEntities.Add(entity, unitofWorkRepository);
        };
    }

    public void AddtoDeletedBundle(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository)
    {
        if (!deletedEntities.ContainsKey(entity)) {
            deletedEntities.Add(entity, unitofWorkRepository);
        }
    }

    public void Commit()
    {
        using (scope = new TransactionScope()) {
            foreach (IAggregateRoot entity in this.addedEntities.Keys) {
                this.addedEntities[entity].HoldOnToCreationOf(entity);
            }

            foreach (IAggregateRoot entity in this.changedEntities.Keys) {
                this.changedEntities[entity].HoldOnToUpdateOf(entity);
            }

            foreach (IAggregateRoot entity in this.deletedEntities.Keys) {
                this.deletedEntities[entity].HoldOnToDeletionOf(entity);
            }

            scope.Complete();
        }
    }
}

现在,域有了一种方式来持有它想要进行的更改,将它们捆绑起来,然后一次性提交它们,作为一个工作单元。通过使工作单元成为我们域进行更改的核心,我们获得了利用数据存储事务的能力。

当您构建其他仓库来检索数据和更改数据存储时,请让它们继承自IUnitOfWork。这样,它们就会内置更改的捆绑和回滚机制,以防任何更改失败。

你的域是一个抽象,一个想法。

通过将工作单元模式纳入你的领域设计,你通过提供一个逻辑机制来工作,从而加入了事务的概念。

© . All rights reserved.