Event SourcingEvent Sourcing — це шаблон проєктування, який представляє стан об'єкта у вигляді множини подій. ПроблемаПотрібно мати доступ до історії змін сутності. Переваги та недолікиПереваги
Недоліки
Зауваження
Опис мовою C#Додамо деякі класи, які будуть симулювати реальні об'єкти. public class User
{
public int Id { get; internal set; }
public string Name { get; internal set; }
public override string ToString()
{
return $"User = {Id} - {Name}";
}
}
Додамо базовий клас для подій public abstract class EventBase
{
public int Id { get; }
public EventBase(int entityId)
{
Id = entityId;
}
public abstract void Apply(User entity);
}
Та декілька реалізацій цих подій: public class UserCreatedEvent : EventBase
{
private readonly int id;
private readonly string name;
public UserCreatedEvent(int id, string name)
: base(id)
{
this.id = id;
this.name = name;
}
public override void Apply(User entity)
{
entity.Id = id;
entity.Name = name;
}
}
public class UserNameChangedEvent : EventBase
{
private readonly string name;
public UserNameChangedEvent(int id, string name)
: base(id)
{
this.name = name;
}
public override void Apply(User entity)
{
entity.Name = name;
}
}
Змінимо нашу сутність таким чином, щоб вона містила інформацію про події: public class User
{
...
internal User() { }
public User(int id, string name)
{
Id = id;
Name = name;
events.Add(new UserCreatedEvent(id, name));
}
public void SetName(string name)
{
Name = name;
events.Add(new UserNameChangedEvent(this.Id, name));
}
private ICollection<EventBase> events = new List<EventBase>();
public IEnumerable<EventBase> Events => events;
}
Та сховище, яке вміє працювати із нашою сутністю та подіями: public class Store
{
private readonly ICollection<EventBase> events = new List<EventBase>();
public void Add(User user)
{
foreach (var domainEvent in user.Events)
{
events.Add(domainEvent);
}
}
public User GetById(int id)
{
var domainEvents = events.Where(e => e.Id == id);
var user = new User();
foreach (var domainEvent in domainEvents)
{
domainEvent.Apply(user);
}
return user;
}
}
Використання цього шаблону не помітне для користувачів: class Program
{
static void Main(string[] args)
{
var store = new Store();
var user = new User(id: 1, name: "John");
user.SetName("Jane");
store.Add(user);
var userFromStore = store.GetById(1);
Console.WriteLine(userFromStore);
}
}
РеалізаціяC#Приклад реалізації на мові С#
using System;
using System.Linq;
using System.Collections.Generic;
namespace EventSourcing
{
public abstract class EventBase
{
public int Id { get; }
public EventBase(int entityId)
{
Id = entityId;
}
public abstract void Affect(User entity);
}
public class User
{
public int Id { get; private set; }
public string Name { get; private set; }
public override string ToString()
{
return $"User = {Id} - {Name}";
}
internal User() { }
public User(int id, string name)
{
Apply(new UserCreatedEvent(id, name));
}
public void SetName(string name)
{
Apply(new UserNameChangedEvent(this.Id, name));
}
public void Apply(UserCreatedEvent userCreatedEvent)
{
this.Id = userCreatedEvent.Id;
this.Name = userCreatedEvent.Name;
events.Add(userCreatedEvent);
}
public void Apply(UserNameChangedEvent userNameChangedEvent)
{
this.Name = userNameChangedEvent.Name;
events.Add(userNameChangedEvent);
}
private ICollection<EventBase> events = new List<EventBase>();
public IEnumerable<EventBase> Events => events;
public void ClearEvents()
{
this.events.Clear();
}
}
public class UserCreatedEvent : EventBase
{
public int Id { get; }
public string Name { get; }
public UserCreatedEvent(int id, string name)
: base(id)
{
this.Id = id;
this.Name = name;
}
public override void Affect(User entity)
{
entity.Apply(this);
}
}
public class UserNameChangedEvent : EventBase
{
public string Name { get; }
public UserNameChangedEvent(int id, string name)
: base(id)
{
this.Name = name;
}
public override void Affect(User entity)
{
entity.Apply(this);
}
}
public class Store
{
private readonly ICollection<EventBase> events = new List<EventBase>();
public void Add(User user)
{
foreach (var domainEvent in user.Events)
{
events.Add(domainEvent);
}
}
public User GetById(int id)
{
var domainEvents = events.Where(e => e.Id == id);
var user = new User();
foreach (var domainEvent in domainEvents)
{
domainEvent.Affect(user);
}
user.ClearEvents();
return user;
}
}
class Program
{
static void Main(string[] args)
{
var store = new Store();
var user = new User(id: 1, name: "John");
user.SetName("Jane");
store.Add(user);
var userFromStore = store.GetById(1);
Console.WriteLine(userFromStore);
}
}
}
Див. такожДжерела
|