ドメイン駆動設計を知らない人でもドメイン駆動設計っぽいコードになる規約を考えた

必要になった経緯

今開発しているシステムはドメイン駆動設計(以下DDD)をベースに開発したコードになってます。 要件に対して、開発人員が圧倒的に足らずSIerさんにお願いすることになったのですが、 SIerは手続型のコードしか書けない人が多く、DDDのコードは書けない状態でした。

自社の社員でもなく、DDDができるようになろうという意識もないので、 DDDのコードを極力壊さずに作ってもらうためのルールが必要でした。

してみたこと

DDDはこう書いてくださいと具体的に掲示できる類でもないと私は思っています。 手ほどきのような基準を決めることが難しいため、 こういうコードは書かないでくださいという禁止事項を掲示して、 逸脱したコードを避ける方向で対応してみました。

コードレビューを通じて、DDDにならないコードの書き方を逐一言語化し、 開発チームにNGコードをフィードバックしていくことに方向を是正していました。

掲示したコード規約

試行したルールと何が困ったのかをいくつか記載します。

Entityクラス以外でのGetterの禁止

例えば、以下のように駅ごとにどのJRが運営している判定するロジックがありました。

JrErea.EAST.equals(JrStationEntity.getJrArea()

上記のようなロジックが書かれることによって、JrStationEntityが知っているJRのエリアをGetterを介して、Entityの外でも知ることができてしまいました。 これによって、以下のような問題が起きました。

  • 判定がいろんなクラスに散らばってしまった
  • サービス層やインフラ層などのドメイン層以外に業務ロジックをGetterを使って書いて、漏れ出した
  • 例示した同じ判定が頻出し、判定ごとにそのロジックが書かれた

Getterが必要になる場合もあるのですが、 現場レベルではGetterを禁止にして、DDDを少し理解してる人にGetterを介していいか判断を仰ぐ形式にしていました。 結果として、問題は減ったので、現場のスキルが育ってない内はGetterは禁止にした方が無難でした。

SQLのWHERE句は検索キーのみであり、抽出ロジックを書かない

DB駆動型の開発をしてる人はデータを編集することが主眼なので、データ操作するSQLに業務ロジックがかかれがちです。 例えば、JR東の駅のみ深夜営業するとか個別のサービスがあった場合に、 未廃止(DELETED_STATION_IDが存在しないと仮定する)東の駅のみを抽出できるようにSQLで条件付けしてしまうことがあります。

WHERE 
  STATION_ID = #{stationId.getValue}
  AND JR_AREA = 'east'
  AND DELETED_STATION_ID IS NOT NULL //廃止された駅IDではない

SQLに条件がでてしまうことによって、ドメイン層に深夜営業は未廃止のJR東の駅のみというロジックがでてこなくなってしまいました。 そのため、ドメインに書くべき業務ロジックが喪失してしまう事象が起きました。 また、検索単位でSQLが作られてしまい、データソース層が肥大化し、 そのため、基本的にIDなどの一意性を指定できるもののみWHERE句に指定してもいいとしました。

  • ID(契約IDなど)
  • いつ(指定年月で開始など)
  • 状態(未変換、最新のXXなど)

フラグを使わない

手続き型に慣れてる人はよく使ってました。 フラグ立ててそのフラグで判定するこんな書き方が多かったです。

boolean JrEastFlg = false;
if(JrStationEntity.isEast()){
  JrEastFlg = true;
}
if(JrEastFlg){
// JR東の時だけ、深夜営業処理
}

処理したいことの判定ロジックをドメイン層以外のサービス層やインフラ層に外に出されてしまう原因になることが多かったです。 再代入しないといけないため、変更漏れや、TRUEになるパターンが追加された場合、バグにつながりやすいコードを書かれやすかったこともあります。 これを避けるため、フラグを使用禁止にし、判定したい目的(ここでは深夜営業要否)のロジックをドメイン(Entity)に作るようにしてもらうことにしました。

コード規約を準備してみた結果

データ駆動であれば、テーブル設計だけできれば開発ができますが、 DDDだと、ドメインモデルやモデルからのクラス構成などの設計に落とし込むことができず、開発ができないことが発生しえます。 これは以下のpospomeさんの発表でも説明されてました。 speakerdeck.com

そのため、チームのスキルレベルが低いのであれば、まずDDDでやらないことを選択肢に入れるべきだと思ってます。

ただ、それでもあえてDDDで開発をしないといけないのであれば、現場にはやってはいけない禁止事項を掲示し、できるだけNGコードを避けます。 明文化した禁止事項にする場合、広くNGにしてしまう場合があるので、 その際はDDDで設計できるエンジニアが判断して許容していく方が望ましかったです。

試した結果、禁止事項を掲示することによって、レビューでNGにする回数は減りました。 また、明文化していくことによって、現場からこの場合は許容されないかなどの指摘もあがり、精査していく効果もありました。

ベストな方法ではないと思いますが、DDDで作ったシステムをなんとか直すのであれば、禁止事項を掲示して、例外にする必要になりそうな場合のみ、DDDを分かる人が判断したり、コードを書いたりするのがベターな方法との結論になりました。