Я создаю библиотечное приложение. Давайте предположим, что у нас есть требование разрешить зарегистрированным людям в библиотеке заимствовать книгу на определенный период времени по умолчанию (4 недели).
Я начал моделировать свой домен с помощью Агрегата Root с именем Loan
с кодом ниже:
public class Loan : AggregateRoot<long>
{
public static int DefaultLoanPeriodInDays = 30;
private readonly long _bookId;
private readonly long _userId;
private readonly DateTime _endDate;
private bool _active;
private Book _book;
private RegisteredLibraryUser _user;
public Book Book => _book;
public RegisteredLibraryUser User => _user;
public DateTime EndDate => _endDate;
public bool Active => _active;
private Loan(long bookId, long userId, DateTime endDate)
{
_bookId = bookId;
_userId = userId;
_endDate = endDate;
_active = true;
}
public static Loan Create(long bookId, long userId)
{
var endDate = DateTime.UtcNow.AddDays(DefaultLoanPeriodInDays);
var loan = new Loan(bookId, userId, endDate);
loan.Book.Borrow();
loan.AddDomainEvent(new LoanCreatedEvent(bookId, userId, endDate));
return loan;
}
public void EndLoan()
{
if (!Active)
throw new LoanNotActiveException(Id);
_active = false;
_book.Return();
AddDomainEvent(new LoanFinishedEvent(Id));
}
}
И моя сущность Book
выглядит следующим образом:
public class Book : Entity<long>
{
private BookInformation _bookInformation;
private bool _inStock;
public BookInformation BookInformation => _bookInformation;
public bool InStock => _inStock;
private Book(BookInformation bookInformation)
{
_bookInformation = bookInformation;
_inStock = true;
}
public static Book Create(string title, string author, string subject, string isbn)
{
var bookInformation = new BookInformation(title, author, subject, isbn);
var book = new Book(bookInformation);
book.AddDomainEvent(new BookCreatedEvent(bookInformation));
return book;
}
public void Borrow()
{
if (!InStock)
throw new BookAlreadyBorrowedException();
_inStock = false;
AddDomainEvent(new BookBorrowedEvent(Id));
}
public void Return()
{
if (InStock)
throw new BookNotBorrowedException(Id);
_inStock = true;
AddDomainEvent(new BookReturnedBackEvent(Id, DateTime.UtcNow));
}
}
Как видите, я использую фабричный метод stati c для создания моего агрегата займа root, где я передаю идентификационную информацию о книге заимствований и идентификацию пользователя, который собирается заимствовать ее. Должен ли я передавать здесь ссылки на эти объекты (книга и пользователь) вместо идентификаторов? Какой подход лучше? Как вы можете видеть, моя сущность Book также имеет свойство, которое указывает на наличие книги (свойство InStock
). Должен ли я обновить это свойство в следующем сценарии использования, например, в обработчике LoadCreatedEvent
? Или это должно быть обновлено здесь в моем Агрегате Root? Если это должно быть обновлено здесь, в моем агрегате, я должен передать всю ссылку на книгу, а не просто идентификатор, чтобы иметь возможность вызывать его метод _book.Borrow()
.
Я застрял в этой точке, потому что хотел бы сделать это довольно правильно с подходом DDD. Или я начинаю делать это не с той стороны, а что-то упускаю или неправильно об этом думаю?