Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
リーダブルコード by DDD / Readable Code by DDD
Search
Yoshiki Iida
July 07, 2021
Programming
21
13k
リーダブルコード by DDD / Readable Code by DDD
リーダブルコード by DDD
モデリングを起点に可読性の高いコードを実現する
Yoshiki Iida
July 07, 2021
Tweet
Share
More Decks by Yoshiki Iida
See All by Yoshiki Iida
スタートアップでゼロからマネジメント文化を作ってきた話 / How I built a management culture from scratch at a startup
yoshikiiida
0
350
自律的なスケーリング手法FASTにおけるVPoEとしてのアカウンタビリティ / dev-productivity-con-2025
yoshikiiida
2
35k
エンジニアリングマネージャー視点での、自律的なスケーリングを実現するFASTという選択肢 / RSGT2025
yoshikiiida
5
12k
ログラスが面白いと思う理由をマネージャーがエモく語ってみる / 20240829 vs LT
yoshikiiida
1
990
質とスピードを両立するログラスのホールチームQA / 20240827 QASaaS_findy
yoshikiiida
2
1.2k
エンジニア組織30人の壁を超えるための 評価システムとマネジメントのスケール / Scaling evaluation system and management
yoshikiiida
12
4.1k
スクラムの成熟と壁 〜スケーリングの議論から見えたもの〜 / Maturity and barriers in Scrum
yoshikiiida
4
2.1k
スタートアップにおける組織設計とスクラムの長期戦略 / Scrum Fest Kanazawa 2024
yoshikiiida
17
6.8k
ログラスの選考プロセスにおけるアトラクト戦略 / Attraction strategy in Loglass interview process
yoshikiiida
8
4.3k
Other Decks in Programming
See All in Programming
守る「だけ」の優しいEMを抜けて、 事業とチームを両方見る視点を身につけた話
maroon8021
3
1.4k
20260313 - Grafana & Friends Taipei #1 - Kubernetes v1.36 的開發雜記:那些困在 Alpha 加護病房太久的 Metrics
tico88612
0
230
Codex の「自走力」を高める
yorifuji
0
1.3k
今年もTECHSCOREブログを書き続けます!
hiraoku101
0
120
AI時代のシステム設計:ドメインモデルで変更しやすさを守る設計戦略
masuda220
PRO
6
1.1k
DevinとClaude Code、SREの現場で使い倒してみた件
karia
1
1.1k
PHPのバージョンアップ時にも役立ったAST(2026年版)
matsuo_atsushi
0
250
今からFlash開発できるわけないじゃん、ムリムリ! (※ムリじゃなかった!?)
arkw
0
150
テレメトリーシグナルが導くパフォーマンス最適化 / Performance Optimization Driven by Telemetry Signals
seike460
PRO
2
160
S3ストレージクラスの「見える」「ある」「使える」は全部違う ─ 体験から見た、仕様の深淵を覗く
ya_ma23
0
1.1k
Claude Code Skill入門
mayahoney
0
430
Takumiから考えるSecurity_Maturity_Model.pdf
gessy0129
1
160
Featured
See All Featured
Why Mistakes Are the Best Teachers: Turning Failure into a Pathway for Growth
auna
0
96
SEO for Brand Visibility & Recognition
aleyda
0
4.4k
Lightning Talk: Beautiful Slides for Beginners
inesmontani
PRO
1
490
Test your architecture with Archunit
thirion
1
2.2k
Building Flexible Design Systems
yeseniaperezcruz
330
40k
Reality Check: Gamification 10 Years Later
codingconduct
0
2.1k
We Have a Design System, Now What?
morganepeng
55
8k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
49
9.9k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
25
1.8k
Java REST API Framework Comparison - PWX 2021
mraible
34
9.2k
How to make the Groovebox
asonas
2
2k
brightonSEO & MeasureFest 2025 - Christian Goodrich - Winning strategies for Black Friday CRO & PPC
cargoodrich
3
130
Transcript
モデリングを起点に可読性の高いコードを実現する 2021/07/07 #readablelt Yoshiki Iida リーダブルコード by DDD
Yoshiki Iida (@ysk_118) エンジニアに始まり、スクラムマスター、プロダクトオーナー、マネージャー、執行 役員を経験し、現場のチームビルディングから部署を超えた会社全体の改善な ど、アジャイルな組織づくりの推進を行ってきました。現在は株式会社ログラスに てソフトウェアエンジニアとしてプロダクト開発に携わっています。 書籍「Scrum Boot Camp
The Book 増補改訂版」コラムニスト。 一般社団法人アジャイルチームを支える会 理事。 $ whoami
ログラスについて は、事業進捗を可視化することで 柔軟で高精度な経営推進を実現する プランニング・クラウドサービスです。
ログラスについて
ログラスについて
• コードがリーダブルであることとDDDの関連 • 実践プラクティス Topic
コードがリーダブルであるとは?
コードがリーダブルであるとは? 他の人が最短時間で理解できること
理解できるとは?
理解できるとは? • 例えば、あるクラスが何をしているか理解できるとは? ◦ 何をしているのかわかる ▪ 状態/振る舞い ◦ 依存関係がわかる ▪
どこから呼ばれているか ▪ 何を呼んでいるか ◦ どんなビジネス的な意味を持っているかわかる
理解できるとは? • 例えば、あるクラスが何をしているか理解できるとは? ◦ 何をしているのかわかる ▪ 状態/振る舞い ◦ 依存関係がわかる ▪
どこから呼ばれているか ▪ 何を呼んでいるか ◦ どんなビジネス的な意味を持っているかわかる ここまでは時間をかけて コードを読み込めば なんとかなるかも これはコードだけでは わからないかもしれない
理解できないとどうなるか? • 実装の手戻り • 実装の歪み ◦ その後の変更可能性を著しく下げる • 品質の低下 ◦
不十分なテスト観点 • 低品質の再生産 ◦ コピペによる増殖
システムを正しく作るためにはどちらも重要 実装の理解しやすさ 実装の目的の理解しやすさ
システムを正しく作るためにはどちらも重要 実装の理解しやすさ 実装の目的の理解しやすさ → DDDというアプローチ
ドメインモデリングから始まる実装 実装の理解しやすさ 実装の目的の理解しやすさ ドメインモデリング 実装 & リファクタリング
ドメインモデリングの位置付け ビジネス的な一次情報 • ユーザーヒアリング • ユースケース ドメインモデリング • ドメインモデル図 仕様・テストケース
• 仕様 • テストケース • 受入基準 ↑ ビジネスとシステムをつなぐ 最も重要なプロセス
ドメインモデリングのサンプル /** * コメント */ class Comment private constructor( val
id: ID<Comment>, val commentText: String, val isResolved: Boolean, val createdAt: DateTime, val updatedAt: DateTime, val createdUserId: ID<User>, val commentType: CommentType, /** コメントの条件 */ val condition: CommentCondition, ) { companion object { fun create( commentText: String, createdUserId: ID<User>, commentType: CommentType, condition: CommentCondition, ): Comment { validationCommentTextLength(commentText) val createdAt = DateTime.now() return Comment( id = ID.gen(), commentText = commentText, isResolved = false, createdAt = createdAt, updatedAt = createdAt, createdUserId = createdUserId, condition = condition, commentType = commentType, ) } } } /** * コメントの条件 */ data class CommentCondition( val departmentId: ID<Department>, val projectId: ID<Project>, val yearMonth: YearMonth, ) ドメインモデル を元に実装する データの実例を併記しておくとわかりやすい
実装の目的の理解しやすさ • ドメインモデル図があれば登場する概念を一目で把握できる • ユースケースがあればそれらの概念がどう使われるのか理解できる • そこから仕様を導き出すことができ、 その仕様を検証するためのテストケースを作成することができる ここを最初に押さえていれば 後からjoinする人も実装の目的は理解しやすい
余談: アンチパターン • 実装の目的が失われてしまったシステムは変更難易度が桁違いになる • 「なぜこの概念が必要なのか・・?」「この依存は必要なのか・・?」「この挙 動でユースケースを満たしているのか・・?」 ◦ コードを考古学して答えが見つかれば良いが最終的にエスパーで妥 協しなければいけないケースもありうる
実装の理解しやすさ • 重要な要素 ◦ 責務 ◦ テスト ◦ リファクタリング
責務 責務とは、そのクラスが行うこと/そのクラスが表すもの • 責務を1つに絞っていくことでリーダブルになること ◦ クラスの関心ごとが小さくなり理解しやすくなる ◦ テスト対象が明確になり、テストコード実装しやすくなる ◦ テストがあるとリファクタリングしやすくなり、コードがさらに読みやすく
なる
責務 例えば、ユースケース層では1クラス1パブリックメソッドにしていくと見通しが良くなる class ProjectService( private val tenantSvc: TenantService, private val
repo: ProjectRepository, ) { fun list( tenantId: ID<Tenant>, projectId: ID<Project> ): List<Project> { val projectList = ~~~ return projectList } fun fix( tenantId: ID<Tenant>, projectId: ID<Project> ) { repo.fix(tenantId, projectId) } fun create( tenantId: ID<Tenant>, projectParam: ProjectParam, ): Project { return repo.create(tenantId, projectParam) } } class ListProjectUseCase( private val tenantSvc: TenantService, private val repo: ProjectRepository, ) { fun execute( tenantId: ID<Tenant>, projectId: ID<Project> ): List<Project> { val projectList = ~~~ return projectList } } class CreateProjectUseCase( private val tenantSvc: TenantService, private val repo: ProjectRepository, ) { fun execute( tenantId: ID<Tenant>, projectParam: ProjectParam, ): Project { return repo.create(tenantId, projectParam) } } class FixProjectUseCase( private val tenantSvc: TenantService, private val repo: ProjectRepository, ) { fun execute( tenantId: ID<Tenant>, projectId: ID<Project> ) { repo.fix(tenantId, projectId) } }
テスト リファクタリングしやすくなる。変更しやすくなる。テストは資産! • テストに関するコメント • テストデータの可視化 • 日本語変数を活用する
テストに関するコメント • 本質的なテストコード以外を小さくし、何をテストしているのかわかりやすく する @Test fun `インサートしたデータが削除できること`() { // given:
val comment = createComment() dataCreators.comment.create(tenant.tenantId, listOf(comment)) // 事前確認: 件数が一件取得される commentRepository.findById(tenant.tenantId, comment.id) .also { assertNotNull(it) } // when: ID指定して削除すると commentRepository.deleteById(tenant.tenantId, comment.id) // then: ID指定で取得できなくなる commentRepository.findById(tenant.tenantId, comment.id) .also { assertNull(it) } } データ準備はprivate method化 when, thenで何をした結果 どうなるかを明確化
テストデータの可視化 • 大きなオブジェクトのテストデータはコメントなどで表現するのは限界がある ◦ 割り切ってスプレッドシートリンクをテストコードに残す internal class ComparisonFlatTreeV3ViewTest { /**
* テストデータの可視化 : https://docs.google.com/spreadsheets/d/1Gd_xxx---xx_XX/edit#gid=xx */ @Test fun`対比表Viewの数値部分 _月毎`() {
日本語変数の活用 • 英語にすると馴染みがなくて可読性が低くなるような場合は日本語変数も アリ ◦ テストコードなどでは有用 val 当期純利益 = result.treeView.items.find
{ it.id == "XXXXXXXXXXXXX" }!! 当期純利益.also { it -> val jan = it.data.find { it.periodKey == "2020-01" }!! assertEquals(expected = BigDecimal(10000), actual = jan.actual.sum) val feb = it.data.find { it.periodKey == "2020-02" }!! assertEquals(expected = BigDecimal(20000), actual = feb.actual.sum) val mar = it.data.find { it.periodKey == "2020-03" }!! assertEquals(expected = BigDecimal(30000), actual = mar.actual.sum) }
リファクタリング 絶え間なく行う • 個人的によくやるもの ◦ 責務を細かく分割する ◦ プリミティブ型やタプルに対して名前をつけた型を用意する ◦ SLAPでメソッドの粒度を揃える
◦ ...
その他 • 見た目の美しさ&統一はIDEとlinterに任せる • 命名はガイドライン化し認知コスト・思考コストを下げる • 参照実装のガイドライン化 ◦ 真似して欲しいものとしてまとめておく •
コメント ◦ 真似して欲しくないものをしっかり書く
命名のガイドライン化 命名がブレやすいものは認知コストにな るためガイドライン化。 変更したければPullRequestベースで 更新していく。
真似して欲しいもの オンボーディング資料に参照実装として 積極的に真似して欲しい実装をまとめて ある
真似して欲しくないもの // TODO: TestXxxFactory実装に寄せる private fun mockDepartment(id: String) = Department.of(
id = ID.from(id), tenantId = tenantId, code = Code.of(id), name = Name.of(id), externalSystemDepartmentCode = null ) 新たに汎用的な仕組みを作ったので 古い書き方がコピペされないように 機械的にTODOを埋め込む override fun insert(tenantId: ID<Tenant>, account: Account) { // TODO: バリデーションはドメイン層に委譲 if (account.accountType.isAggregationType()) throw BadRequestException("科目種別はSTANDARD_PL_AGGRには設定できません ") insert(listOf(account)) } このレイヤの責務 でないものを明示
• 変更しやすいシステムの特徴 ◦ リーダブルであること ▪ 実装を理解しやすいこと ▪ 実装の目的を理解しやすいこと • 実践アプローチ
◦ ドメインモデリングは実装の目的理解にダイレクトに有効 ◦ 責務を意識し、テスト・リファクタを回していくことで 結果的にリーダブルになっていく ◦ 参照実装などを充実させることでチーム開発がより効率化 まとめ