使用 .NET Core 实现 CQRS 和事件溯源

作者:微信公众号:【架构师老卢】
7-3 15:14
153

在复杂的软件体系结构领域,命令查询责任分离 (CQRS) 和事件溯源 (ES) 模式已成为处理高度可扩展和可维护应用程序的强大技术。本文使用 .NET Core 深入探讨这些高级模式,以银行应用程序为上下文提供实用见解和实际示例。

了解 CQRS

原理概述

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 和事件溯源

实施策略

结合 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 应用程序的全部潜力。

相关留言评论
昵称:
邮箱:
阅读排行