English version.
Читать на сайте.
Возвращаюсь к работе после отпуска.
В ноябре я рассказывал про EventFlow — библиотеку, предназначенную для DDD и Event Sourcing. Наша команда активно её использует. Спустя несколько месяцев работы с ней я заметил, что часто приходится писать повторяющийся код, что довольно утомительно.
// Чтобы создать событие для агрегата OrderAggregate,
// приходится каждый раз указывать тип агрегата и тип его ID
public class OrderCreated :
IAggregateEvent<OrderAggregate, OrderAggregateId>
// При подписке на события необходимо
// указывать тип события, агрегата и ID
public class OrderAggregateSubscribers :
ISubscribeSynchronousTo<OrderAggregate, OrderAggregateId, OrderCreated>
🔴Как избежать бойлерплейта?
Можно объявить абстрактный класс для всех событий агрегата и использовать его:
public abstract class OrderAggregateEvent :
IAggregateEvent<OrderAggregate, OrderAggregateId>;
Создать новый интерфейс, принимающий только тип события:
public interface ISubscribeSynchronousTo<TEvent> :
ISubscribeSynchronousTo<OrderAggregate, OrderAggregateId, TEvent>
Но даже в этом случае всё равно приходится вручную дублировать код для каждого агрегата. А как гласит известная айтишная мудрость:
«Никогда не тратьте 10 минут, чтобы сделать что-то вручную, если можно потратить 10 часов пытаясь это автоматизировать».
Поэтому в начале февраля я занялся написанием генератора кода для EventFlow. Через 2 недели и 7 коммитов библиотека EventFlow.SourceGenerators была готова и на днях Расмус Микелсен (автор EventFlow) принял изменения.
🔴Как помогают генераторы кода?
Теперь вся рутинная работа ложится на компилятор, и можно писать меньше кода, который к тому же лучше читается.
// Добавляем атрибут AggregateExtensions к агрегату
[AggregateExtensions]
public class OrderAggregate(OrderAggregateId id) :
AggregateRoot<OrderAggregate, OrderAggregateId>(id)
// Объявляем события
// OrderAggregateEvent создан автоматически
public class OrderCreated : OrderAggregateEvent;
// Подписываемся на события
// Новый интерфейс создан автоматически
public class OrderAggregateSubscribers :
ISubscribeSynchronousTo<OrderCreated>
Как видите, код стал заметно чище, и теперь нам не приходится снова и снова указывать тип агрегата и тип ID.
🔴Сложности при работе с генераторами кода
Стоит отметить, что генераторы кода — непростая тема. Существует множество ограничений и сложностей. Например, с чем я столкнулся:
1. Генераторы можно писать только под .NET Standard 2.0.
2. Возможны коллизии названий сгенерированных объектов.
3. Тестирование сгенерированного кода имеет свои особенности.
🔴Что дальше?
Если вам интересна тема генераторов кода, рекомендую начать с этого видео от Microsoft.
Затем изучите следующие материалы:
- Creating an incremental generator.
- Introducing C# Source Generators.
- Incremental Generators Cookbook.
В качестве примера можно заглядывать в исходный код библиотеки EventFlow.SourceGenerators и её тестов. Она довольна простая. Больше примеров её применения можно найти в документации.