DDD實踐:領域事件
阿新 • • 發佈:2019-01-02
face factor microsoft scope logger mediator ber holding !=
要求:修改good表,添加 organization
基礎定義
用於引發和調度事件的延遲方法 AddDomainEvent
Domain\SeedWork\Entity.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.Domain\Event\StockAddedToGoodsDomainEvent.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
DDD實踐:領域事件