Skip to content

Commit

Permalink
fix
Browse files Browse the repository at this point in the history
  • Loading branch information
sadnessOjisan committed Feb 7, 2024
1 parent 7ba0da0 commit da4bb01
Showing 1 changed file with 7 additions and 20 deletions.
27 changes: 7 additions & 20 deletions src/contents/20240208-do-you-use-entity/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ isFavorite: false
isProtect: false
---

定期的に DDD やらクリーンアーキテクチャなどを題材にした記事が盛り上がっているのを見ていると、いま長年の疑問を書けば答えてくれるのではと思って書いてみる。
定期的に DDD やクリーンアーキテクチャなどを題材にした記事が盛り上がっているのを見ていると、いま長年の疑問を書けば答えてくれるのではと思って書いてみる。
何に困っているかというと、

- いわゆるレポジトリ層が持つ create/update 関数の引数は Entity で待ち受けるべきか、プレーンなオブジェクトで待ち受けるべきか分からない
- ユーザーから POST Body されたデータにはビジネスルールを適用させるべきか(= 一度 Entity を作るべきか)分からない

だ。

OGP は最近ハマっているネギ油だ。自分の設計はだいたいこんな感じだ
OGP は最近ハマっているネギ油だ。汚い同心円は自分の設計に通ずるものがある

## 謝罪

Expand All @@ -34,7 +34,7 @@ OGP は最近ハマっているネギ油だ。自分の設計はだいたいこ
ここでいうレイヤードアーキテクチャとは、クリーンアーキテクチャ本に出てくる同心円の図のアレを指した言葉だと思ってくれて良い。

- Entity が中心にあり、ビジネスルールを持つ。
- その外側にユースケースやサービスがアプリケーションのロジックを持つ
- その外側のレイヤーがアプリケーションのロジックを持つ
- その外側に HTTP 通信や DB との IO といった機能を持つ。
- 内側は外側の事情を知ってはいけない。

Expand Down Expand Up @@ -99,7 +99,7 @@ class Repository {
Entity は同一性の比較のために id を必要とするからだ。
LastIDを取るとなると、複数ユーザーが同時に create userすることを考えて、ロックを取る仕組みを作ったり、単純に通信が1往復増えるので嬉しくない。
実際ISUCONでもここを潰すと予選突破できる年があった。
なので auto incrementな場合はこの手法は使えない
なので auto incrementな場合はこの手法を使いたくない動機が生まれる

一方で ID 生成に制限がないのであればアプリケーション側で UUID を作って、それをもとに Entity を作れば解決できると思う。
けど primary key を UUID にするのは容量・検索・ソート・パフォーマンスの面から問題がないとは言えないので使いたくない。
Expand Down Expand Up @@ -132,7 +132,7 @@ class Repository {
こうすれば id は不要で、DBの id 生成にお任せできる。

ただ、こうすると今度はビジネスルールの適用が Entity 経由でできなくなる。
きっとコードとしては、
その検証をしたければそのロジックをどこかにおく必要があり、きっとコードとしては、

```ts
const userService = new UserService(new UserRepository(mysqlDriver));
Expand All @@ -141,18 +141,6 @@ router.post("users", (req, res) => {
userService.save({ name, age });
});

class UserSerivice {
private: _repo: UserRepository

save({name, age}: UserInput){
this._repo.save({name, age})
}
}
```

といった感じになるだろうが、この設計に 20歳以上かどうかという検証をさせたいのであれば、おそらく

```ts
class UserSerivice {
private: _repo: UserRepository

Expand All @@ -174,8 +162,7 @@ class UserSerivice {
これはこれでドメインモデル貧血症などと呼ばれたりしてよくないものとされている。
それに加えてEntityにロジックがあれば、生成と検証が必ずセットになるので、こういうロジックはEntityに書いておいたほうが良いだろう。

ただ、TypeScript に限って話せば、そもそも脱クラスという設計もあって、そういう Entity をただの型定義として定義してしまう設計手法もあるので、絶対にないとは言い切れない設計ではある。
後述する Opaque を使うことで生成と検証を必ずセットにできる。
そして、Plain なオブジェクトを引数に取るべきかが分からなくなった。

## データを更新するときにビジネスルールを適用する方法がわからない

Expand All @@ -191,7 +178,7 @@ class UserSerivice {

## 自分なりの解決策

いわゆるレイヤードアーキテクチャで実装した時にはいつも上記のことに頭を悩ましているが、これまでに自分がとった解決策も紹介しようと思う
いわゆるレイヤードアーキテクチャで実装した時にはいつも上記のことに頭を悩ましているが、これまでに自分がとった解決策もあるので紹介しようと思う

### サブクラス

Expand Down

0 comments on commit da4bb01

Please sign in to comment.