TL;DR
- デシリアライズはワークフローの前、シリアライズはワークフローの後につなげる。
- DTO は他の境界づけられたコンテキストとの契約になるので、変更には慎重であるべきだ。シリアライズの方法はライブラリ任せにせず、自分たちで管理する。
内容
第11章 Serializationの一部である。
ワークフローの前後にステップを足す
ワークフローの前にデシリアライズのステップを、後にシリアライズのステップを追加する。たとえば、次のワークフローを考える。
type MyInputType = ...
type MyOutputType = ...
type Workflow = MyInputType -> MyOutputTypeこのとき、デシリアライズのステップは、JSON 文字列を DTO に変換する関数と、DTO を入力の型に変換する関数の 2 つになる。
type JsonString = string
type MyInputDto = ...
type DeserializeInputDto = JsonString -> MyInputDto
type InputDtoToDomain = MyInputDto -> MyInputType同じように、シリアライズのステップは、出力の型を DTO に変換する関数と、DTO を JSON 文字列に変換する関数になる。
type MyOutputDto = ...
type OutputDtoFromDomain = MyOutputType -> MyOutputDto
type SerializeOutputDto = MyOutputDto -> JsonStringこれらの関数は 1 本のパイプラインでつながる。インフラストラクチャ層に公開されるのは、この関数である。
let workflowWithSerialization jsonString =
jsonString
|> deserializeInputDto // JSON から DTO
|> inputDtoToDomain // DTO からドメインオブジェクト
|> workflow // ドメインのコアワークフロー
|> outputDtoFromDomain // ドメインオブジェクトから DTO
|> serializeOutputDto // DTO から JSON境界づけられたコンテキスト間の契約としての DTO
ワークフローが受け取るコマンドは、他の境界づけられたコンテキストの出力であるドメインイベントによってトリガされる。そして、このワークフローの出力が、さらに別の境界づけられたコンテキストの入力になる。
このドメインイベントとコマンドによって、境界づけられたコンテキストが従うべき契約の形が決まる(境界づけられたコンテキスト間の契約)。境界づけられたコンテキスト同士の密結合は避けたいので、この契約は厳しいものではない。それでも、DTO を変更するときは慎重であるべきだ。
そのためには、ライブラリの暗黙の変換に任せず、DTO を自分たちで管理する必要がある。大事なのは、シリアライズされる形をこちらでコントロールすることである。
キーワード
自分の理解
DTO の変更に慎重であるべきなのは、DTO がコマンドをシリアライズしたフォーマットであり、変更が他のコンテキストに影響を及ぼしうるからだと理解した(ここは少し自信がない)。
「シリアライズライブラリを使うな」ということではない。シリアライズされる形をこちらでコントロールすることが大事なのであって、実際にシリアライズの全体例では Newtonsoft.Json を使っている。