Подход, который я обычно использую для DDD в F #, заключается в создании типа для каждой отдельной комбинации бизнес-правил, которые я хочу применить:
[<Struct>] type MonetaryUnitCount = private MonetaryUnitCount of int
Эти типы имеют частные конструкторы,таким образом, мы можем контролировать, как они создаются, предоставляя только одну функцию для создания каждого типа:
module MonetaryUnitCount =
let create count =
if count < 0
then Error "Count must be positive"
else Ok (MonetaryUnitCount count)
Тогда каждый тип записи также будет иметь закрытый конструктор и соответствующую функцию create
, которая вызывает правильныйcreate
функция для каждого поля, проверка данных по мере их поступления:
type Money =
private {
OneCentCount: MonetaryUnitCount
TenCentCount: MonetaryUnitCount
QuarterCount: MonetaryUnitCount
OneDollarCount: MonetaryUnitCount
FiveDollarCount: MonetaryUnitCount
TwentyDollarCount: MonetaryUnitCount
}
module Money =
let create (a, b, c, d, e, f) =
MonetaryUnitCount.create a
|> Result.bind (fun m -> MonetaryUnitCount.create b |> Result.map (fun n -> m, n))
|> Result.bind (fun (m, n) -> MonetaryUnitCount.create c |> Result.map (fun o -> m, n, o))
|> Result.bind (fun (m, n, o) -> MonetaryUnitCount.create d |> Result.map (fun p -> m, n, o, p))
|> Result.bind (fun (m, n, o, p) -> MonetaryUnitCount.create e |> Result.map (fun q -> m, n, o, p, q))
|> Result.bind (fun (m, n, o, p, q) -> MonetaryUnitCount.create f |> Result.map (fun r -> m, n, o, p, q, r))
|> Result.map (fun (m, n, o, p, q, r) ->
{
OneCentCount = m
TenCentCount = n
QuarterCount = o
OneDollarCount = p
FiveDollarCount = q
TwentyDollarCount = r
})
Таким образом, вы либо получите успешный результат с заполненным Money
, либо ошибку с ошибкой проверки.Нет способа создать экземпляр MonetaryUnitCount
с отрицательным значением, и нет способа создать экземпляр Money
с недопустимым MonetaryUnitCount
, поэтому любой существующий экземпляр должен быть действительным.
Этот синтаксис может быть значительно упрощен при использовании Вычислительного выражения для автоматической привязки типа Result
или улучшен при использовании аппликативов для сбора всех ошибок проверки.