在复杂的软件体系结构领域,命令查询责任分离 (CQRS) 和事件溯源 (ES) 模式已成为处理高度可扩展和可维护应用程序的强大技术。本文使用 .NET Core 深入探讨这些高级模式,以银行应用程序为上下文提供实用见解和实际示例。
CQRS 将系统的读取和写入操作分离到不同的模型中,从而允许优化查询和命令。这种分离可以提高性能、可伸缩性和可维护性。
在命令模型中,您可以处理修改应用程序状态的操作。这涉及创建命令,这些命令封装了执行操作所需的所有信息,以及执行逻辑的处理程序。
示例:银行应用程序 — 处理存款
public class DepositFundsCommand
{
public Guid AccountId { get; set; }
public decimal Amount { get; set; }
}
命令处理程序:
public class DepositFundsHandler : ICommandHandler<DepositFundsCommand>
{
private readonly IEventStore _eventStore;
public DepositFundsHandler(IEventStore eventStore)
{
_eventStore = eventStore;
}
public async Task Handle(DepositFundsCommand command)
{
var transactionEvent = new TransactionEvent
{
TransactionId = Guid.NewGuid(),
AccountId = command.AccountId,
Amount = command.Amount,
Timestamp = DateTime.UtcNow
};
await _eventStore.SaveEventAsync(transactionEvent);
}
}
查询模型针对读取数据进行了优化。它涉及创建表示数据检索需求的查询和执行检索的处理程序。
示例:银行应用程序 — 检索账户余额
public class AccountBalanceQuery
{
public Guid AccountId { get; set; }
}
public class AccountBalanceDto
{
public Guid AccountId { get; set; }
public decimal Balance { get; set; }
}
查询处理程序:
public class AccountBalanceHandler : IQueryHandler<AccountBalanceQuery, AccountBalanceDto>
{
private readonly IEventStore _eventStore;
public AccountBalanceHandler(IEventStore eventStore)
{
_eventStore = eventStore;
}
public async Task<AccountBalanceDto> Handle(AccountBalanceQuery query)
{
var events = await _eventStore.GetEventsAsync(query.AccountId);
var account = Account.Rebuild(events);
return new AccountBalanceDto
{
AccountId = account.Id,
Balance = account.Balance
};
}
}
事件溯源可确保对应用程序状态的所有更改都存储为一系列事件。这种方法不仅提供审计跟踪,还允许通过重播事件来重建状态。
事件模型捕获有关状态更改的基本信息。每个事件都表示系统中发生的重大更改。
示例:银行应用程序 - 事务事件
public class TransactionEvent
{
public Guid TransactionId { get; set; }
public Guid AccountId { get; set; }
public decimal Amount { get; set; }
public DateTime Timestamp { get; set; }
}
事件存储负责保存和检索事件。它充当应用程序状态的主要事实来源。
public class EventStore : IEventStore
{
private readonly List<TransactionEvent> _events = new List<TransactionEvent>();
public async Task SaveEventAsync(TransactionEvent transactionEvent)
{
_events.Add(transactionEvent);
await Task.CompletedTask;
}
public async Task<List<TransactionEvent>> GetEventsAsync(Guid accountId)
{
return await Task.FromResult(_events.Where(e => e.AccountId == accountId).ToList());
}
}
通过重播事件存储中的事件,可以重新生成实体的当前状态。
public class Account
{
public Guid Id { get; set; }
public decimal Balance { get; set; }
public void Apply(TransactionEvent transactionEvent)
{
Balance += transactionEvent.Amount;
}
public static Account Rebuild(IEnumerable<TransactionEvent> events)
{
var account = new Account();
foreach (var e in events)
{
account.Apply(e);
}
return account;
}
}
用法:
var events = await eventStore.GetEventsAsync(accountId);
var account = Account.Rebuild(events);
结合 CQRS 和事件溯源,命令模型可以通过存储事件来更新状态,而查询模型则通过重播事件来读取状态。
具有事件溯源的命令处理程序:
public class DepositFundsHandler : ICommandHandler<DepositFundsCommand\>
{
private readonly IEventStore _eventStore;
public DepositFundsHandler(IEventStore eventStore)
{
_eventStore = eventStore;
}
public async Task Handle(DepositFundsCommand command)
{
var transactionEvent = new TransactionEvent
{
TransactionId = Guid.NewGuid(),
AccountId = command.AccountId,
Amount = command.Amount,
Timestamp = DateTime.UtcNow
};
await _eventStore.SaveEventAsync(transactionEvent);
}
}
查询处理程序重建状态:
public class AccountBalanceHandler : IQueryHandler<AccountBalanceQuery, AccountBalanceDto>
{
private readonly IEventStore _eventStore;
public AccountBalanceHandler(IEventStore eventStore)
{
_eventStore = eventStore;
}
public async Task<AccountBalanceDto> Handle(AccountBalanceQuery query)
{
var events = await _eventStore.GetEventsAsync(query.AccountId);
var account = Account.Rebuild(events);
return new AccountBalanceDto
{
AccountId = account.Id,
Balance = account.Balance
};
}
}
在 .NET Core 中实现 CQRS 和事件溯源可以显著增强应用程序的可伸缩性、可维护性和可靠性。通过分离命令和查询并将状态更改存储为事件,可以创建一个更易于管理、扩展和调试的系统。采用这些模式,以释放 .NET Core 应用程序的全部潜力。