На чем следует сфокусировать тестирование при разработке приложения по методологии DDD (Domain-Driven Design)?

Ответ

При разработке по DDD тестирование должно быть сфокусировано на доменном слое (Domain Layer), так как именно он содержит ключевую бизнес-логику, правила и инварианты, которые являются ядром и главной ценностью приложения.

Приоритеты тестирования (от высшего к низшему):

  1. Агрегаты (Aggregates): Это главный объект тестирования. Тесты должны проверять:

    • Соблюдение инвариантов: Агрегат не должен переходить в некорректное состояние. Например, сумма заказа не может быть отрицательной.
    • Корректность выполнения команд: Все методы, изменяющие состояние агрегата, должны работать правильно.
    • Генерацию доменных событий: После выполнения действия агрегат должен порождать правильные события (например, OrderPlaced, ItemAdded).
  2. Объекты-значения (Value Objects): Тесты проверяют их валидацию при создании и неизменяемость (immutability).

  3. Доменные сервисы (Domain Services): Тестируется логика, которая координирует действия между несколькими агрегатами и не принадлежит ни одному из них.

Пример теста для агрегата Order:

func TestOrder_AddItem_EnforcesInvariants(t *testing.T) {
    // Arrange: создаем заказ
    order := NewOrder("order-123")

    // Act: добавляем товар
    err := order.AddItem("product-abc", 2, 150.0)

    // Assert: проверяем результат
    if err != nil {
        t.Fatalf("Неожиданная ошибка при добавлении товара: %v", err)
    }

    // Проверяем, что инвариант (общая стоимость) соблюдается
    if order.Total() != 300.0 {
        t.Errorf("Ожидаемая стоимость 300.0, получено %f", order.Total())
    }

    // Проверяем, что было сгенерировано доменное событие
    events := order.Events()
    if len(events) != 1 || events[0].Name != "ItemAddedToOrder" {
        t.Error("Ожидалось событие ItemAddedToOrder")
    }
}

Тесты для других слоев (Application, Infrastructure) также важны, но они вторичны. Инфраструктурный слой (например, репозитории) часто покрывается интеграционными тестами, а не юнит-тестами.

Ответ 18+ 🔞

А, слушай, вот эта ваша DDD, блядь, — это ж надо же так мозги вынести! Но если разобраться, то главная фишка — это доменный слой, ёпта. Вся соль, вся бизнес-логика, все эти ваши правила, которые деньги приносят, — они там, в этом самом домене, как червяк в яблоке. Вот на нём и надо сосредоточиться, а не хуйнёй страдать!

Так, смотри, куда бить в первую очередь, по приоритету:

  1. Агрегаты (Aggregates) — это святое, блядь! Вот их и долби тестами, как будто от этого жизнь зависит. Смотри, что проверять:

    • Инварианты не нарушены? Чтобы заказ, например, в минус не ушёл, а то будет пиздец, а не бизнес.
    • Команды работают? Вызвал метод — состояние изменилось так, как надо, а не как попало.
    • События порождаются? Сделал что-то — агрегат должен пискнуть правильным событием, типа OrderPlaced. Если молчит — значит, хуйня, а не агрегат.
  2. Объекты-значения (Value Objects). Тут проще: создался — и навеки застыл, как памятник. Проверь, что при создании всё валидно, и что его после этого не сломаешь.

  3. Доменные сервисы (Domain Services). Это когда логика не влезает в один агрегат, а болтается между ними, как манда с ушами. Вот её и тестируй, чтобы координация была чёткой.

Вот, смотри, как это примерно выглядит на коде, чтобы не быть просто болтуном:

func TestOrder_AddItem_EnforcesInvariants(t *testing.T) {
    // Подготовка: создаём заказ, пока ещё пустой
    order := NewOrder("order-123")

    // Действие: пытаемся впихнуть товар
    err := order.AddItem("product-abc", 2, 150.0)

    // Проверка: а не обосрались ли мы?
    if err != nil {
        t.Fatalf("Неожиданная ошибка при добавлении товара: %v", err)
    }

    // Считаем бабки: инвариант должен быть в порядке
    if order.Total() != 300.0 {
        t.Errorf("Ожидаемая стоимость 300.0, получено %f", order.Total())
    }

    // А событие-то выстрелило? Должно!
    events := order.Events()
    if len(events) != 1 || events[0].Name != "ItemAddedToOrder" {
        t.Error("Ожидалось событие ItemAddedToOrder, а тут какая-то хуйня!")
    }
}

Остальные слои — прикладной, инфраструктурный — это, конечно, тоже надо тестировать, но это уже как бы второй эшелон. Инфраструктуру (типа репозиториев к базе) часто вообще интеграционными тестами проверяют, а не этими мелкими юнитами. Но домен — это священная корова, его надо вылизать до блеска, блядь!