Функциональный способ DDD: почему лучше отделить состояние от поведения при применении DDD с функциональным языком? - PullRequest
5 голосов
/ 01 июля 2019

Я прочитал несколько статей (также книгу Functional domain modeling), где они предлагают отделить состояние объекта домена от поведения, но я не могу понять преимущества такого подхода по сравнению с моделью достижимого домена.

Вот пример модели расширенного домена:

case class Account(id: AccountId, balance: Money) {
  def activate: Account = {
   // check if it is already active, eg, enforce invariant 
   ...
  }
  def freeze: Account = ???
} 

Я могу связать операции с этим аккаунтом следующим образом:

account.activate.freeze

Вот пример «анемичного» подхода, который они предлагают:

case class Account(id: AccountId, balance: Money)

object AccountService {
  def activate =  (account: Account) => {
   // check if it is already active, eg, enforce invariant 
    ...
  }

  def freeze =  (account: Account) =>   {
    ...     
  }
}

И здесь я могу связать операции таким образом

activate andThen freeze apply account

В чем преимущество второго подхода, кроме "элегантного" синтаксиса?

Кроме того, в случае модели расширенной области я буду применять инварианты в одном классе, но в случае «анемичной» модели логика / инварианты могут распространяться на службы

Ответы [ 2 ]

2 голосов
/ 01 июля 2019

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

object AccountService {
  def fraud = (account: Account) => ...
}

тогда мы могли бы составить этот шаг примерно так

(fraud andThen activate andThen freeze)(account)

Концептуально, добавление fraud шага проверки не изменило структуру модели домена case class Account, так зачем же его перекомпилировать? Это форма разделения интересов, где мы хотим сузить изменения в кодовой базе до минимальной релевантной части.

1 голос
/ 02 июля 2019

Я предлагаю два мыслительных процесса, которые могут помочь объяснить эту загадку:


Концепция state в вашем примере и книге различаются. (Надеюсь, мыоба имеют отношение к функциональному и реактивному моделированию домена ).

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


Функциональное программирование - это реализация поведения, которое не зависит от данныхпереданы в них.

При реализации такого поведения следует отметить два аспекта.

Поведение может быть повторно использовано в разных контекстах. Это может быть абстрактная чертамоноид, если хотите, который принимает любой тип T и выполняет ту же операцию над ним.В вашем примере freeze может быть таким поведением, применимым к Account, Loan, Balance и т. Д.

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

Комбинируя две точки, можно сказать, что имеет смысл реализовать поведение в качестве фрагмента кода многократного использования в различных контекстах (например, Service), гарантируя, что входные данные проверены (т. Е. Проверяют состояние объекта, предоставленного в качестве входных данных перед обработкой).

Представляя приемлемое состояние объекта в виде отдельного типа и параметризовав модель / объектс этим явным типом мы могли бы принудительно выполнять статическую проверку ввода во время компиляции.Ссылаясь на пример, приведенный в книге, вы можете только approve andThen enrich.Любая другая неправильная последовательность вызовет ошибку во время компиляции, что гораздо предпочтительнее, чем использование защитных средств защиты для проверки ввода во время выполнения.

Таким образом, второй подход - это не просто элегантный синтаксис в конце дня.Это механизм для создания проверок во время компиляции, основанный на состоянии объекта.


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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...