ДОМЕННЫЙ ДИЗАЙН

Практический DDD на Голанге: Спецификация

Пример использования универсального шаблона, используемого при проверке, создании и запросе - Спецификация.

Не так много структур кода, которые приносят мне радость, когда мне нужно их написать. Впервые я реализовал такой код с облегченным ORM на Go, когда у нас его не было.

С другой стороны, я использовал ORM много лет. В какой-то момент, когда вы зависите от ORM, использование QueryBuilder неизбежно. Здесь вы можете заметить такие термины, как предикаты. Вот где мы можем найти шаблон Спецификации.

Трудно найти какой-либо шаблон, который мы использовали бы в качестве спецификации, но мы не слышим его названия. Я думаю, что сложнее всего написать приложение без использования этого шаблона.

Спецификация имеет множество приложений. Мы можем использовать его для запросов, создания или проверки. Мы можем предоставить уникальный код, который может выполнять всю эту работу, или предоставить разные реализации для разных вариантов использования.

Other articles from DDD series:
1. Practical DDD in Golang: Value Object
2. Practical DDD in Golang: Entity
3. Practical DDD in Golang: Domain Service
4. Practical DDD in Golang: Domain Event
5. Practical DDD in Golang: Module
6. Practical DDD in Golang: Aggregate
7. Practical DDD in Golang: Factory
8. Practical DDD in Golang: Repository


Для проверки

Первый вариант использования шаблона спецификации - это проверка. В первую очередь мы проверяем данные в формах, но это на уровне представления. Иногда мы делаем это во время создания, например, для Объектов-ценностей.

В контексте уровня домена мы можем использовать спецификации для проверки состояний сущностей и фильтрации их из коллекции. Итак, проверка на уровне домена уже имеет более широкое значение, чем для пользовательского ввода.

В приведенном выше примере есть интерфейс ProductSpecification. Он определяет только один метод, IsValid, который ожидает экземпляры Product и в результате возвращает логическое значение, если Product проходит правила проверки.

Простая реализация этого интерфейса - HasAtLeast, проверяющая минимальное количество Product. Более интересными валидаторами являются две функции, IsPlastic и IsDeliverable.

Мы можем обернуть эти функции конкретным типом FunctionSpecification. Этот тип включает функцию с такой же сигнатурой, что и два упомянутых. Кроме того, он предоставляет методы, соответствующие ProductSpecification интерфейсу.

Этот пример является хорошей особенностью Go, где мы можем определить функцию как тип и присоединить к нему метод, чтобы он мог неявно реализовать некоторый интерфейс. У нас есть тот случай, когда он представляет метод IsValid, который выполняет встроенную функцию.

Кроме того, существует еще одна уникальная Спецификация AndSpecification. Такая структура помогает нам использовать объект, который реализует интерфейс theProductSpecification, но группирует валидацию из всех включенных спецификаций.

В приведенном выше фрагменте кода мы можем найти две дополнительные спецификации. Один из них OrSpecification, и он, как и AndSpecification, выполняет все спецификации, которые он содержит. Просто в этом случае он использует алгоритм or вместо and.

Последний - NotSpecification, который отрицает результат встроенной спецификации. NotSpecification тоже может быть функциональной спецификацией, но я не хотел ее слишком усложнять.

Для запроса

Я уже упоминал в этой статье о применении паттерна Спецификация как части ORM. Во многих случаях вам не нужно будет реализовывать спецификации для этого варианта использования, по крайней мере, если вы используете какой-либо ORM.

Отличные реализации Спецификации в виде предикатов я нашел в библиотеке Ent от Facebook. С того момента у меня не было варианта использования для написания спецификации для запросов.

Тем не менее, когда вы обнаружите, что ваш запрос для репозитория на уровне домена может быть слишком сложным, вам понадобится больше возможностей для фильтрации желаемых объектов. Реализация может выглядеть как на примере ниже.

В новой реализации интерфейс ProductSpecification предоставляет два метода: Query и Values. Мы используем их для получения строки запроса для конкретной спецификации и возможных значений, которые она содержит.

И снова мы видим дополнительную спецификацию AndSpecification и OrSpecification. В этом случае они присоединяются ко всем базовым запросам, в зависимости от оператора, который они представляют, и объединяют все значения.

Наличие такой Спецификации на уровне домена вызывает сомнения. Как вы можете видеть из выходных данных, спецификации предоставляют синтаксис SQLike, в котором слишком много технических деталей.

В этом случае, вероятно, решением будет определение интерфейсов для различных спецификаций на уровне домена и фактических реализаций на уровне инфраструктуры.

Или реструктурировать код так, чтобы спецификация содержала информацию об имени поля, операции и значении. Затем, чтобы иметь некоторый сопоставитель на уровне инфраструктуры, который может сопоставить такую ​​спецификацию с запросом SQL.

Для создания

Одним из простых вариантов использования спецификации является создание сложного объекта, который может сильно различаться. В таких случаях мы можем комбинировать его с шаблоном Factory или использовать внутри службы домена.

В приведенном выше примере мы можем найти третью реализацию для спецификации. В этом сценарии ProductSpecification поддерживает один метод, Create, который ожидает Product, адаптирует его и возвращает обратно.

Еще раз, есть AndSpecification, чтобы применить изменения, определенные из нескольких Спецификаций, но нет OrSpecification. Мне не удалось найти реальный вариант использования or алгоритма во время создания объекта.

Даже если его нет, мы можем ввести NotSpecification, который может работать с определенными типами данных, такими как логические. Тем не менее, в этом небольшом примере я не смог найти для него подходящего варианта.

Заключение

Спецификация - это шаблон, который мы используем везде, во многих разных случаях. Сегодня нелегко обеспечить проверку на уровне домена без использования спецификаций.

Спецификацию мы также можем использовать при запросе объектов из базового хранилища. Сегодня они являются частью ORM. Третье использование - для создания сложных экземпляров, где мы можем комбинировать его с шаблоном Factory.

Other articles from DDD series:
1. Practical DDD in Golang: Value Object
2. Practical DDD in Golang: Entity
3. Practical DDD in Golang: Domain Service
4. Practical DDD in Golang: Domain Event
5. Practical DDD in Golang: Module
6. Practical DDD in Golang: Aggregate
7. Practical DDD in Golang: Factory
8. Practical DDD in Golang: Repository

Полезные ресурсы: