DDD实践:领域事件

目录(?)[+]

 

要求:修改good表,添加 organization

基础定义

用于引发和调度事件的延迟方法 AddDomainEvent

DomainSeedWorkEntity.cs

public abstract class Entity<T>
    {
        int? _requestedHashCode;
        T _Id;        
        public virtual  T Id 
        {
            get
            {
                return _Id;
            }
            protected set
            {
                _Id = value;
            }
        }

        private List<INotification> _domainEvents;
        public IReadOnlyCollection<INotification> DomainEvents => _domainEvents?.AsReadOnly();

        public void AddDomainEvent(INotification eventItem)
        {
            _domainEvents = _domainEvents ?? new List<INotification>();
            _domainEvents.Add(eventItem);
        }

        public void RemoveDomainEvent(INotification eventItem)
        {
            _domainEvents?.Remove(eventItem);
        }

        public void ClearDomainEvents()
        {
            _domainEvents?.Clear();
        }

        public bool IsTransient()
        {
            return Id.ToString() == default(T).ToString();
        }

        public override bool Equals(object obj)
        {
            if (obj == null || !(obj is Entity<T>))
                return false;

            if (Object.ReferenceEquals(this, obj))
                return true;

            if (this.GetType() != obj.GetType())
                return false;

            Entity<T> item = (Entity<T>)obj;

            if (item.IsTransient() || this.IsTransient())
                return false;
            else
                return item.Id.ToString() == this.Id.ToString();
        }

        public override int GetHashCode()
        {
            if (!IsTransient())
            {
                if (!_requestedHashCode.HasValue)
                    _requestedHashCode = this.Id.GetHashCode() ^ 31; // XOR for random distribution (http://blogs.msdn.com/b/ericlippert/archive/2011/02/28/guidelines-and-rules-for-gethashcode.aspx)

                return _requestedHashCode.Value;
            }
            else
                return base.GetHashCode();

        }
        public static bool operator ==(Entity<T> left, Entity<T> right)
        {
            if (Object.Equals(left, null))
                return (Object.Equals(right, null)) ? true : false;
            else
                return left.Equals(right);
        }

        public static bool operator !=(Entity<T> left, Entity<T> right)
        {
            return !(left == right);
        }
    }

聚合根

Goods.cs

  public class Goods: Entity<Guid>, IAggregateRoot
    {
        
        public string CreatedBy { get; private set; }
        public DateTime? CreatedTime { get; private set; }

        public string ModifiedBy { get; private set; }

        public DateTime? ModifiedTime { get; private set; }

        public string GoodsName { get; private set; }
        public int? GoodsNum { get; private set; }



        public Goods(string createdBy, DateTime? createdTime, string modifiedBy, DateTime? modifiedTime, string goodsName, int? goodsNum)
        {
            CreatedBy = createdBy;
            CreatedTime = createdTime;
            ModifiedBy = modifiedBy;
            ModifiedTime = modifiedTime;
            GoodsName = goodsName;
            GoodsNum = goodsNum;
        }

        public void StockAddedToGoods()
        {
            AddDomainEvent(new StockAddedToGoodsDomainEvent());  //加入事件列表
        }


        public void SetModified()
        {
            ModifiedBy = "aaaaa";
            ModifiedTime = DateTime.Now;
        }
    }

请注意 AddDomainEvent 方法的唯一功能是将事件添加到列表。 尚未调度任何事件,尚未调用任何事件处理程序。
你需要在稍后将事务提交到数据库时调度事件。 如果使用 Entity Framework Core,意味着在 EF DbContext 的 SaveChanges 方法中,如以下示例所示:

// EF Core DbContext
public class SampleContext : DbContext, IUnitOfWork
{
    // ...
    public async Task<bool> SaveEntitiesAsync(CancellationToken cancellationToken = default(CancellationToken))
    {
        // Dispatch Domain Events collection.
        // Choices:
        // A) Right BEFORE committing data (EF SaveChanges) into the DB. This makes
        // a single transaction including side effects from the domain event
        // handlers that are using the same DbContext with Scope lifetime
        // B) Right AFTER committing data (EF SaveChanges) into the DB. This makes
        // multiple transactions. You will need to handle eventual consistency and
        // compensatory actions in case of failures.        
        await _mediator.DispatchDomainEventsAsync(this);

        // After this line runs, all the changes (from the Command Handler and Domain
        // event handlers) performed through the DbContext will be committed
        var result = await base.SaveChangesAsync();
    }

}

Organization.cs

    public class Organization: 
        Entity<Guid>, IAggregateRoot
    {
        public string CreatedBy { get; set; }
        public DateTime CreatedTime { get; set; }
        public string ModifiedBy { get; set; }
        public DateTime ModifiedTime { get; set; }

        public string Name { get; set; }

        private Organization() { }

        public Organization(Guid id, string name, string createdBy, DateTime createdTime, string modifiedBy,DateTime modifiedTime)
        {
            Id = id;
            Name = name;
            CreatedBy = createdBy;
            ModifiedBy = modifiedBy;
            ModifiedTime = modifiedTime;

            AddDomainEvent(new OrganizationCreatedDomainEvent(name, id));
        }

    }

CQRS

1.创建命令

    [DataContract]
    public class UpdateGoodsAndAddStockCommand : IRequest<bool>
    {
        [DataMember]
        public Guid Id { get; set; }
        [DataMember]
        public string CreatedBy { get; private set; }

        [DataMember]
        public string GoodsName { get; private set; }
        [DataMember]
        public int GoodsNum { get; private set; }
    }

2.创建处理

    public class UpdateGoodsAndAddStockCommandHandler: IRequestHandler<UpdateGoodsAndAddStockCommand, bool>
    {
        private readonly IGoodsRepository _goodsRepository;
        private readonly IMediator _mediator;

        public UpdateGoodsAndAddStockCommandHandler(IGoodsRepository goodsRepository, IMediator mediator)
        {
            _goodsRepository = goodsRepository;
            _mediator = mediator;
        }


        public async Task<bool> Handle(UpdateGoodsAndAddStockCommand request, CancellationToken cancellationToken)
        {
            var good = await _goodsRepository.GetAsync(request.Id);

            
            good.SetModified(); //赋值修改人和修改时间,
            good.StockAddedToGoods(); //添加 Stock 事件
              return await _goodsRepository.UnitOfWork.SaveEntitiesAsync();
        }
    }

3.使用 IoC 的域事件调度程序

public class MediatorModule : Autofac.Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            builder.RegisterAssemblyTypes(typeof(IMediator).GetTypeInfo().Assembly)
                .AsImplementedInterfaces();

            // Register all the Command classes (they implement IRequestHandler) in assembly holding the Commands
            builder.RegisterAssemblyTypes(typeof(CreateOrderCommand).GetTypeInfo().Assembly)
                .AsClosedTypesOf(typeof(IRequestHandler<,>));

           //这里
builder.RegisterAssemblyTypes(typeof(UpdateGoodsAndAddStockCommand).GetTypeInfo().Assembly)
           .AsClosedTypesOf(typeof(IRequestHandler<,>))
 
        // Other registrations ...
    }
}                          

事件列表

Sample.DomainEventStockAddedToGoodsDomainEvent.cs

   public class StockAddedToGoodsDomainEvent: INotification
    {
        public Organization Organization { get; }


        public StockAddedToGoodsDomainEvent()
        {
            Organization = new Organization(Guid.NewGuid(), "tss","tss", DateTime.Now,null,DateTime.Now);
        }
    }

事件处理:StockAddedToGoodsDomainEventHandler

    public class StockAddedToGoodsDomainEventHandler : INotificationHandler<StockAddedToGoodsDomainEvent>
    {
        private readonly IOrganizationRepository _iorganizationRepository;
        private readonly ILoggerFactory _logger;
        private readonly ISampleIntegrationEventService _sampleIntegrationEventService;

        public StockAddedToGoodsDomainEventHandler(IOrganizationRepository iorganizationRepository, ILoggerFactory logger)
        {
            _iorganizationRepository = iorganizationRepository;
            _logger = logger;
        }

        public async Task Handle(Domain.Events.StockAddedToGoodsDomainEvent notification, CancellationToken cancellationToken)
        {
            _iorganizationRepository.Add(notification.Organization);

            var organization = await _iorganizationRepository.GetAsync(notification.Organization.Id);
            await _iorganizationRepository.UnitOfWork.SaveEntitiesAsync();
            _logger.CreateLogger(nameof(StockAddedToGoodsDomainEventHandler))
             .LogTrace($"Stock with Id: {organization.Id} has been successfully Created");
        }
    }

资料:

microsoft.doc 域事件:设计和实现
微软的官方.NET CORE微服务示例eShopOnContainers

原文地址:https://www.cnblogs.com/webenh/p/11434783.html