Я разрабатываю систему электронного кошелька с использованием доменно-ориентированного проектирования (DDD), и передо мной стоит проблема, связанная с разработкой моих агрегатов. В частности, у меня есть агрегат, представляющий электронный кошелек, который содержит набор транзакций. Список транзакций потенциально может со временем расти бесконечно, и я не уверен, как лучше всего справиться с этой неограниченной коллекцией в агрегате.
Вот упрощенный пример моей текущей совокупной структуры:
public class Wallet {
private String walletId;
private List<Transaction> transactions;
}
public class Transaction {
private String transactionId;
private UsdAmount amount;
}
Учитывая, что список транзакций может стать довольно большим, меня беспокоят потенциальные проблемы с производительностью и масштабируемостью. Я также не уверен, как сохранить целостность агрегата при работе с этой неограниченной коллекцией.
Примечание. Я не могу рассматривать транзакции как отдельный агрегат, поскольку им не хватает значительной бизнес-логики или поведения.
Пытаясь решить эту проблему, я экспериментировал с идеей создания доменной службы специально для управления операциями только для чтения над сущностями транзакций. Эта служба домена будет инкапсулировать такие операции, как запрос, фильтрация и получение транзакций, связанных с кошельком. Передавая эту службу домена любому агрегатному поведению, которое должно работать со списком транзакций, я стремился имитировать отложенную загрузку и минимизировать сложность самого агрегата Wallet.
Хотя подход с использованием доменной службы для операций транзакций только для чтения кажется многообещающим, я не уверен в его долгосрочных последствиях и в том, соответствует ли он лучшим практикам в DDD. Меня особенно беспокоят потенциальные недостатки, такие как усиление связи между службой домена и агрегатом кошелька.
Я ищу отзывы и мнения сообщества о том, считается ли этот подход хорошей практикой в DDD и существуют ли альтернативные стратегии для более эффективного управления неограниченными коллекциями внутри агрегатов. Любые рекомендации или предложения будут с благодарностью приняты.
Учитывая, что список транзакций может стать довольно большим, меня беспокоят потенциальные проблемы с производительностью и масштабируемостью. Я также не уверен, как сохранить целостность агрегата при работе с этой неограниченной коллекцией.
Прежде всего напомним: потенциальные проблемы с производительностью и масштабируемостью не являются реальными проблемами производительности и масштабируемости. Может иметь смысл «просто» выпустить MVP с простым дизайном и отложить решение проблемы до тех пор, пока у вас не будет достаточно трафика, чтобы мотивировать работу (см. Джефф Дин, 2009).
Механическое решение, которое я часто вижу в системах с источником событий, — это создание снимков. По сути, вы «проецируете» информацию, которую вам нужно запомнить, в документ/отчет/снимок с метаданными, описывающими, в какой точке потока был создан отчет, а затем для обработки новых сообщений вы загружаете этот документ вместе со всеми последующими сообщениями. события.
Если вы немного подумаете об этом, то поймете, что это то, что вы уже делаете, в том смысле, что при обработке всех ваших событий создается неявный снимок того, как выглядел агрегат до появления каких-либо событий. Мы просто берем эту концепцию и (а) делаем ее явной, (б) делаем ее сохраняемой и (в) вводим возможность запуска с некоторой точки, отличной от начала потока.
(Примечание: эти снимки обычно создаются асинхронно — нам не нужно блокировать и обновлять снимок при блокировке и обновлении потока событий.)
Решение предметной области, которое я часто вижу, делает время явным в определении агрегата; вместо того, чтобы вечно отслеживать каждое событие в истории, мы разбиваем их по финансовым кварталам (например), используя процессы для переноса информации из одного финансового квартала в другой.
Другими словами, вы заменяете неограниченные доменные процессы последовательностью доменных процессов, ограниченных по времени.