Введение в Event Sourcing
Event Sourcing — это шаблон проектирования, который набирает популярность в мире разработки программного обеспечения, и не зря. Он позволяет фиксировать изменения состояния приложения в виде последовательности событий, а не просто хранить текущее состояние. Этот подход обеспечивает полный аудит всех изменений, упрощая отладку, аудит и даже возврат к предыдущим состояниям при необходимости.
Что такое EventStoreDB?
EventStoreDB — это база данных, предназначенная для хранения, обработки и доставки изменений состояния приложений, известных как события. Она предлагает такие функции, как журнал событий только для добавления, потоки для организации и ускорения извлечения событий, подписки и соединители для доставки событий во внешние системы, а также проекции для преобразования и фильтрации событий.
Настройка EventStoreDB
Прежде чем углубляться в реализацию на Go, давайте быстро настроим EventStoreDB. Вы можете запустить EventStoreDB локально или использовать управляемую службу, такую как Event Store Cloud.
Запуск EventStoreDB локально
Чтобы запустить EventStoreDB локально, вы можете использовать Docker:
docker run -d --name eventstoredb -p 2113:2113 -p 1113:1113 eventstore/eventstore:latest
Эта команда запускает экземпляр EventStoreDB на вашем локальном компьютере.
Подключение к EventStoreDB из Go
Чтобы подключиться к EventStoreDB из приложения Go, вам нужно использовать клиентский пакет SDK EventStoreDB. Вот как вы можете создать клиента и подключиться к базе данных:
package main
import (
"context"
"fmt"
"log"
"github.com/eventstore/eventstore-client-go/v3/esdb"
)
func main() {
ctx := context.Background()
client, err := esdb.NewClient(esdb.ConnectionSettings{
Addresses: []string{"esdb://localhost:2113?tls=false"},
})
if err != nil {
log.Fatal(err)
}
defer client.Close()
fmt.Println("Подключено к EventStoreDB")
}
Реализация Event Sourcing в Go
Для более надёжной реализации Event Sourcing в Go вы можете использовать библиотеку, подобную библиотеке источников событий Go, которая использует дженерики Go для типобезопасных и многократно используемых компонентов.
Использование библиотеки источников событий Go
Вот пример того, как вы можете использовать эту библиотеку для управления агрегатами и применения событий:
Управление агрегатом
package main
import (
"context"
"fmt"
"log"
"github.com/thefabric-io/eventsourcing"
)
type ToDoItem struct {
ID string
Tasks []string
Completed bool
}
type ToDoItemCreated struct {
ID string
}
type TaskAdded struct {
Task string
}
type ToDoItemCompleted struct{}
func (t *ToDoItem) Apply(event eventsourcing.Event) error {
switch e := event.(type) {
case ToDoItemCreated:
t.ID = e.ID
case TaskAdded:
t.Tasks = append(t.Tasks, e.Task)
case ToDoItemCompleted:
t.Completed = true
default:
return fmt.Errorf("unknown event type: %T", event)
}
return nil
}
func main() {
ctx := context.Background()
// Инициализируем хранилище событий
eventStore, err := eventsourcing.NewPostgreSQLEventStore(ctx, "your_db_connection_string", "your_schema_name")
if err != nil {
log.Fatal(err)
}
// Создаём новый элемент списка дел
toDoItem := &ToDoItem{}
events := []eventsourcing.Event{
ToDoItemCreated{ID: "123"},
TaskAdded{Task: "Купить молоко"},
TaskAdded{Task: "Выгулять собаку"},
ToDoItemCompleted{},
}
// Применяем события к агрегату
for _, event := range events {
err := toDoItem.Apply(event)
if err != nil {
log.Fatal(err)
}
}
// Сохраняем события в хранилище событий
err = eventStore.Save(ctx, toDoItem.ID, events)
if err != nil {
log.Fatal(err)
}
fmt.Println("Элемент списка дел:", toDoItem)
}
Диаграмма последовательности для Event Sourcing
Вот диаграмма последовательности, иллюстрирующая процесс создания и применения событий к агрегату с использованием библиотеки источников событий Go:
Использование EventStoreDB с библиотекой источников событий Go
Чтобы интегрировать EventStoreDB с библиотекой источников событий Go, вам необходимо реализовать интерфейс хранилища событий, предоставляемый библиотекой, используя EventStoreDB.
Пример реализации
Вот пример того, как вы можете реализовать интерфейс хранилища событий с помощью EventStoreDB:
package main
import (
"context"
"fmt"
"log"
"github.com/eventstore/eventstore-client-go/v3/esdb"
"github.com/thefabric-io/eventsourcing"
)
type eventStoreDB struct {
client *esdb.Client
}
func (e *eventStoreDB) Save(ctx context.Context, aggregateID string, events []eventsourcing.Event) error {
streamName := fmt.Sprintf("aggregate-%s", aggregateID)
var eventDatas []esdb.EventData
for _, event := range events {
eventData, err := eventsourcing.ToEventData(event)
if err != nil {
return err
}
eventDatas = append(eventDatas, eventData)
}
_, err := e.client.AppendToStream(ctx, streamName, esdb.AppendToStreamOptions{}, eventDatas...)
return err
}
func main() {
ctx := context.Background()
client, err := esdb.NewClient(esdb.ConnectionSettings{
Addresses: []string{"esdb://localhost:2113?tls=false"},
})
if err != nil {
log.Fatal(err)
}
defer client.Close()
eventStore := &eventStoreDB{client: client}
// Используйте хранилище событий, как показано в предыдущем примере
}
Заключение
Реализация шаблона проектирования Event Sourcing в приложении Go с использованием EventStoreDB обеспечивает надёжный и масштабируемый способ управления состоянием приложения. Используя библиотеку источников событий Go и интегрируя её с EventStoreDB, вы можете гарантировать, что изменения состояния вашего приложения будут эффективно зафиксированы и сохранены.
Помните, что Event Sourcing — это не только хранение событий; это создание прозрачной, поддающейся проверке и легко обслуживаемой системы. Имея под рукой нужные инструменты и немного креативности, вы сможете создавать системы, которые не только функциональны, но и приятны в работе.
Заключительные мысли
Начиная свой путь по реализации Event Sourcing, помните, что это паттерн, требующий тщательного планирования и исполнения. Но результаты стоят затраченных усилий. Итак, вперёд, погрузитесь в мир Event Sourcing и наблюдайте, как состояние вашего приложения разворачивается, словно прекрасно написанная история.
И как всегда, счастливого программирования!