DDD, агрегирование Root и объекты в сценарии применения библиотеки - PullRequest
0 голосов
/ 05 апреля 2020

Я создаю библиотечное приложение. Давайте предположим, что у нас есть требование разрешить зарегистрированным людям в библиотеке заимствовать книгу на определенный период времени по умолчанию (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. Или я начинаю делать это не с той стороны, а что-то упускаю или неправильно об этом думаю?

1 Ответ

1 голос
/ 05 апреля 2020

DomainEvents - это события в памяти, которые обрабатываются в пределах одного домена.

Вы фиксируете или откатываете всю «Транзакцию» вместе. Рассматривайте событие домена как DTO, которое должно содержать всю информацию, связанную с тем, что только что произошло в домене. Таким образом, пока у вас есть эта информация, я не думаю, что это имеет значение, если вы передадите Id или все object.

Я бы go для передачи id в событии домена, хотя как эта информация достаточна для передачи информации в DomainEventHandler.

Кроме того, обратитесь к этому примеру аналогичного сценария в Документах Microsoft , где они только передают UserId и CardTypeId вместе со всей другой соответствующей информацией в событии Домена.

public class OrderStartedDomainEvent : INotification {
public string UserId { get; }
public int CardTypeId { get; }
public string CardNumber { get; }
public string CardSecurityNumber { get; }
public string CardHolderName { get; }
public DateTime CardExpiration { get; }
public Order Order { get; }

public OrderStartedDomainEvent(Order order,
                               int cardTypeId, string cardNumber,
                               string cardSecurityNumber, string cardHolderName,
                               DateTime cardExpiration)
{
    Order = order;
    CardTypeId = cardTypeId;
    CardNumber = cardNumber;
    CardSecurityNumber = cardSecurityNumber;
    CardHolderName = cardHolderName;
    CardExpiration = cardExpiration;
} }
...