@k2works/claude-code-booster 3.5.0 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +42 -42
- package/bin/claude-code-booster +90 -90
- package/lib/assets/.claude/README.md +239 -239
- package/lib/assets/.claude/scripts/generate-inception-deck.mjs +911 -911
- package/lib/assets/.claude/settings.json +11 -11
- package/lib/assets/.claude/skills/ai-agent-guidelines/SKILL.md +111 -111
- package/lib/assets/.claude/skills/analyzing-architecture/SKILL.md +83 -83
- package/lib/assets/.claude/skills/analyzing-business/SKILL.md +95 -95
- package/lib/assets/.claude/skills/analyzing-data-model/SKILL.md +77 -77
- package/lib/assets/.claude/skills/analyzing-domain-model/SKILL.md +117 -88
- package/lib/assets/.claude/skills/analyzing-inception-deck/SKILL.md +84 -84
- package/lib/assets/.claude/skills/analyzing-non-functional/SKILL.md +95 -95
- package/lib/assets/.claude/skills/analyzing-operation/SKILL.md +95 -95
- package/lib/assets/.claude/skills/analyzing-requirements/SKILL.md +91 -91
- package/lib/assets/.claude/skills/analyzing-tech-stack/SKILL.md +101 -101
- package/lib/assets/.claude/skills/analyzing-test-strategy/SKILL.md +89 -89
- package/lib/assets/.claude/skills/analyzing-ui-design/SKILL.md +80 -80
- package/lib/assets/.claude/skills/analyzing-usecases/SKILL.md +72 -72
- package/lib/assets/.claude/skills/creating-adr/SKILL.md +113 -113
- package/lib/assets/.claude/skills/developing-backend/SKILL.md +100 -100
- package/lib/assets/.claude/skills/developing-frontend/SKILL.md +93 -93
- package/lib/assets/.claude/skills/developing-release/SKILL.md +120 -120
- package/lib/assets/.claude/skills/generating-slides/SKILL.md +94 -94
- package/lib/assets/.claude/skills/git-commit/SKILL.md +81 -81
- package/lib/assets/.claude/skills/killing-processes/SKILL.md +44 -44
- package/lib/assets/.claude/skills/operating-backup/SKILL.md +59 -59
- package/lib/assets/.claude/skills/operating-cicd/SKILL.md +54 -54
- package/lib/assets/.claude/skills/operating-deploy/SKILL.md +67 -67
- package/lib/assets/.claude/skills/operating-docs/SKILL.md +219 -219
- package/lib/assets/.claude/skills/operating-provision/SKILL.md +77 -77
- package/lib/assets/.claude/skills/operating-setup/SKILL.md +63 -63
- package/lib/assets/.claude/skills/orchestrating-analysis/SKILL.md +104 -104
- package/lib/assets/.claude/skills/orchestrating-development/SKILL.md +162 -161
- package/lib/assets/.claude/skills/orchestrating-operation/SKILL.md +158 -158
- package/lib/assets/.claude/skills/orchestrating-project/SKILL.md +144 -144
- package/lib/assets/.claude/skills/planning-releases/SKILL.md +119 -119
- package/lib/assets/.claude/skills/syncing-github-project/SKILL.md +151 -151
- package/lib/assets/.claude/skills/tracking-progress/SKILL.md +91 -91
- package/lib/assets/.claude/skills/validating-iteration-plan/SKILL.md +215 -215
- package/lib/assets/.devcontainer/devcontainer.json +34 -34
- package/lib/assets/.env.example +17 -17
- package/lib/assets/.gitattributes +4 -4
- package/lib/assets/.github/workflows/docker-publish.yml +77 -77
- package/lib/assets/.github/workflows/mkdocs.yml +39 -39
- package/lib/assets/AGENTS.md +94 -94
- package/lib/assets/CLAUDE.md +183 -183
- package/lib/assets/README.md +254 -254
- package/lib/assets/docker-compose.yml +33 -33
- package/lib/assets/docs/adr/index.md +10 -10
- package/lib/assets/docs/article/functional-desgin-ppp/all/01-immutability-and-data-transformation.md +475 -475
- package/lib/assets/docs/article/functional-desgin-ppp/all/02-function-composition.md +519 -519
- package/lib/assets/docs/article/functional-desgin-ppp/all/03-polymorphism.md +537 -537
- package/lib/assets/docs/article/functional-desgin-ppp/all/04-data-validation.md +300 -300
- package/lib/assets/docs/article/functional-desgin-ppp/all/05-property-based-testing.md +320 -320
- package/lib/assets/docs/article/functional-desgin-ppp/all/06-tdd-and-functional.md +498 -498
- package/lib/assets/docs/article/functional-desgin-ppp/all/07-composite-pattern.md +298 -298
- package/lib/assets/docs/article/functional-desgin-ppp/all/08-decorator-pattern.md +291 -291
- package/lib/assets/docs/article/functional-desgin-ppp/all/09-adapter-pattern.md +336 -336
- package/lib/assets/docs/article/functional-desgin-ppp/all/10-strategy-pattern.md +303 -303
- package/lib/assets/docs/article/functional-desgin-ppp/all/11-command-pattern.md +286 -286
- package/lib/assets/docs/article/functional-desgin-ppp/all/12-visitor-pattern.md +322 -322
- package/lib/assets/docs/article/functional-desgin-ppp/all/13-abstract-factory-pattern.md +319 -319
- package/lib/assets/docs/article/functional-desgin-ppp/all/14-abstract-server-pattern.md +365 -365
- package/lib/assets/docs/article/functional-desgin-ppp/all/15-gossiping-bus-drivers.md +156 -156
- package/lib/assets/docs/article/functional-desgin-ppp/all/16-payroll-system.md +178 -178
- package/lib/assets/docs/article/functional-desgin-ppp/all/17-video-rental-system.md +312 -312
- package/lib/assets/docs/article/functional-desgin-ppp/all/18-concurrency-system.md +287 -287
- package/lib/assets/docs/article/functional-desgin-ppp/all/19-wa-tor-simulation.md +286 -286
- package/lib/assets/docs/article/functional-desgin-ppp/all/20-pattern-interactions.md +274 -274
- package/lib/assets/docs/article/functional-desgin-ppp/all/21-best-practices.md +294 -294
- package/lib/assets/docs/article/functional-desgin-ppp/all/22-oo-to-fp-migration.md +337 -337
- package/lib/assets/docs/article/functional-desgin-ppp/all/index.md +388 -388
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/01-immutability-and-data-transformation.md +273 -273
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/02-function-composition.md +380 -380
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/03-polymorphism.md +384 -384
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/04-clojure-spec.md +350 -350
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/05-property-based-testing.md +352 -352
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/06-tdd-in-functional.md +383 -383
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/07-composite-pattern.md +529 -529
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/08-decorator-pattern.md +395 -395
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/09-adapter-pattern.md +399 -399
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/10-strategy-pattern.md +485 -485
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/11-command-pattern.md +566 -566
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/12-visitor-pattern.md +567 -567
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/13-abstract-factory-pattern.md +475 -475
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/14-abstract-server-pattern.md +462 -462
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/15-gossiping-bus-drivers.md +325 -325
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/16-payroll-system.md +401 -401
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/17-video-rental-system.md +450 -450
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/18-concurrency-system.md +475 -475
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/19-wator-simulation.md +739 -739
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/20-pattern-interactions.md +567 -567
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/21-best-practices.md +518 -518
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/22-oo-to-fp-migration.md +532 -532
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/index.md +241 -241
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/01-immutability-and-data-transformation.md +383 -383
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/02-function-composition.md +374 -374
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/03-polymorphism.md +375 -375
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/04-data-validation.md +195 -195
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/05-property-based-testing.md +268 -268
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/06-tdd-and-fp.md +294 -294
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/07-effects-and-pure-functions.md +164 -164
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/08-error-handling-strategies.md +168 -168
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/09-io-and-external-systems.md +254 -254
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/10-concurrency-patterns.md +269 -269
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/11-command-pattern.md +148 -148
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/12-visitor-pattern.md +176 -176
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/13-abstract-factory-pattern.md +604 -604
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/14-abstract-server-pattern.md +729 -729
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/15-gossiping-bus-drivers.md +291 -291
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/16-payroll-system.md +420 -420
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/17-video-rental-system.md +319 -319
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/18-concurrency-system.md +466 -466
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/19-wator-simulation.md +523 -523
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/20-pattern-interactions.md +287 -287
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/21-best-practices.md +340 -340
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/22-oo-to-fp-migration.md +395 -395
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/index.md +248 -248
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/01-immutability-and-data-transformation.md +384 -384
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/02-function-composition.md +452 -452
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/03-polymorphism.md +495 -495
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/04-data-validation.md +416 -416
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/05-property-based-testing.md +382 -382
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/06-tdd-functional.md +687 -687
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/07-composite-pattern.md +442 -442
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/08-decorator-pattern.md +479 -479
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/09-adapter-pattern.md +479 -479
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/10-strategy-pattern.md +427 -427
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/11-command-pattern.md +428 -428
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/12-visitor-pattern.md +339 -339
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/13-abstract-factory-pattern.md +309 -309
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/14-abstract-server-pattern.md +596 -596
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/15-gossiping-bus-drivers.md +355 -355
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/16-payroll-system.md +350 -350
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/17-video-rental-system.md +414 -414
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/18-concurrency-system.md +367 -367
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/19-wator-simulation.md +403 -403
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/20-pattern-interactions.md +291 -291
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/21-best-practices.md +324 -324
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/22-oo-to-fp-migration.md +332 -332
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/index.md +274 -274
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/01-immutability-and-data-transformation.md +298 -298
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/02-function-composition.md +304 -304
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/03-polymorphism.md +362 -362
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/04-data-validation.md +257 -257
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/05-property-based-testing.md +254 -254
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/06-tdd-functional.md +283 -283
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/07-composite-pattern.md +395 -395
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/08-decorator-pattern.md +319 -319
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/09-adapter-pattern.md +382 -382
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/10-strategy-pattern.md +287 -287
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/11-command-pattern.md +303 -303
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/12-visitor-pattern.md +326 -326
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/13-abstract-factory-pattern.md +332 -332
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/14-abstract-server-pattern.md +379 -379
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/15-gossiping-bus-drivers.md +177 -177
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/16-payroll-system.md +219 -219
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/17-video-rental-system.md +244 -244
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/18-concurrency-system.md +363 -363
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/19-wator-simulation.md +438 -438
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/20-pattern-interactions.md +325 -325
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/21-best-practices.md +403 -403
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/22-oo-to-fp-migration.md +469 -469
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/index.md +174 -174
- package/lib/assets/docs/article/functional-desgin-ppp/index.md +90 -90
- package/lib/assets/docs/article/functional-desgin-ppp/rust/01-immutability-and-data-transformation.md +450 -450
- package/lib/assets/docs/article/functional-desgin-ppp/rust/02-function-composition.md +463 -463
- package/lib/assets/docs/article/functional-desgin-ppp/rust/03-polymorphism.md +425 -425
- package/lib/assets/docs/article/functional-desgin-ppp/rust/04-data-validation.md +273 -273
- package/lib/assets/docs/article/functional-desgin-ppp/rust/05-property-based-testing.md +247 -247
- package/lib/assets/docs/article/functional-desgin-ppp/rust/06-tdd-and-functional.md +841 -841
- package/lib/assets/docs/article/functional-desgin-ppp/rust/07-composite-pattern.md +384 -384
- package/lib/assets/docs/article/functional-desgin-ppp/rust/08-decorator-pattern.md +383 -383
- package/lib/assets/docs/article/functional-desgin-ppp/rust/09-adapter-pattern.md +339 -339
- package/lib/assets/docs/article/functional-desgin-ppp/rust/10-strategy-pattern.md +331 -331
- package/lib/assets/docs/article/functional-desgin-ppp/rust/11-command-pattern.md +356 -356
- package/lib/assets/docs/article/functional-desgin-ppp/rust/12-visitor-pattern.md +379 -379
- package/lib/assets/docs/article/functional-desgin-ppp/rust/13-abstract-factory-pattern.md +361 -361
- package/lib/assets/docs/article/functional-desgin-ppp/rust/14-abstract-server-pattern.md +392 -392
- package/lib/assets/docs/article/functional-desgin-ppp/rust/15-gossiping-bus-drivers.md +300 -300
- package/lib/assets/docs/article/functional-desgin-ppp/rust/16-payroll-system.md +297 -297
- package/lib/assets/docs/article/functional-desgin-ppp/rust/17-video-rental-system.md +304 -304
- package/lib/assets/docs/article/functional-desgin-ppp/rust/18-concurrency-system.md +315 -315
- package/lib/assets/docs/article/functional-desgin-ppp/rust/19-wator-simulation.md +311 -311
- package/lib/assets/docs/article/functional-desgin-ppp/rust/20-pattern-interactions.md +304 -304
- package/lib/assets/docs/article/functional-desgin-ppp/rust/21-best-practices.md +336 -336
- package/lib/assets/docs/article/functional-desgin-ppp/rust/22-oo-to-fp-migration.md +349 -349
- package/lib/assets/docs/article/functional-desgin-ppp/rust/index.md +243 -243
- package/lib/assets/docs/article/functional-desgin-ppp/scala/01-immutability-and-data-transformation.md +328 -328
- package/lib/assets/docs/article/functional-desgin-ppp/scala/02-function-composition.md +348 -348
- package/lib/assets/docs/article/functional-desgin-ppp/scala/03-polymorphism.md +357 -357
- package/lib/assets/docs/article/functional-desgin-ppp/scala/04-data-validation.md +364 -364
- package/lib/assets/docs/article/functional-desgin-ppp/scala/05-property-based-testing.md +515 -515
- package/lib/assets/docs/article/functional-desgin-ppp/scala/06-tdd-functional.md +557 -557
- package/lib/assets/docs/article/functional-desgin-ppp/scala/07-composite-pattern.md +363 -363
- package/lib/assets/docs/article/functional-desgin-ppp/scala/08-decorator-pattern.md +327 -327
- package/lib/assets/docs/article/functional-desgin-ppp/scala/09-adapter-pattern.md +517 -517
- package/lib/assets/docs/article/functional-desgin-ppp/scala/10-strategy-pattern.md +441 -441
- package/lib/assets/docs/article/functional-desgin-ppp/scala/11-command-pattern.md +407 -407
- package/lib/assets/docs/article/functional-desgin-ppp/scala/12-visitor-pattern.md +379 -379
- package/lib/assets/docs/article/functional-desgin-ppp/scala/13-abstract-factory-pattern.md +398 -398
- package/lib/assets/docs/article/functional-desgin-ppp/scala/14-abstract-server-pattern.md +476 -476
- package/lib/assets/docs/article/functional-desgin-ppp/scala/15-gossiping-bus-drivers.md +391 -391
- package/lib/assets/docs/article/functional-desgin-ppp/scala/16-payroll-system.md +342 -342
- package/lib/assets/docs/article/functional-desgin-ppp/scala/17-video-rental-system.md +324 -324
- package/lib/assets/docs/article/functional-desgin-ppp/scala/18-concurrency-system.md +730 -730
- package/lib/assets/docs/article/functional-desgin-ppp/scala/19-wator-simulation.md +624 -624
- package/lib/assets/docs/article/functional-desgin-ppp/scala/20-pattern-interactions.md +512 -512
- package/lib/assets/docs/article/functional-desgin-ppp/scala/21-best-practices.md +433 -433
- package/lib/assets/docs/article/functional-desgin-ppp/scala/22-oo-to-fp-migration.md +688 -688
- package/lib/assets/docs/article/functional-desgin-ppp/scala/index.md +243 -243
- package/lib/assets/docs/article/getting-start-tdd/clojure/01-todo-list-and-first-test.md +166 -166
- package/lib/assets/docs/article/getting-start-tdd/clojure/02-fake-it-and-triangulation.md +162 -162
- package/lib/assets/docs/article/getting-start-tdd/clojure/03-obvious-implementation-and-refactoring.md +135 -135
- package/lib/assets/docs/article/getting-start-tdd/clojure/04-version-control-and-conventional-commits.md +88 -88
- package/lib/assets/docs/article/getting-start-tdd/clojure/05-package-management-and-static-analysis.md +299 -299
- package/lib/assets/docs/article/getting-start-tdd/clojure/06-task-runner-and-ci-cd.md +241 -241
- package/lib/assets/docs/article/getting-start-tdd/clojure/07-protocols-and-records.md +131 -131
- package/lib/assets/docs/article/getting-start-tdd/clojure/08-multimethods-and-design-patterns.md +130 -130
- package/lib/assets/docs/article/getting-start-tdd/clojure/09-namespaces-and-module-design.md +127 -127
- package/lib/assets/docs/article/getting-start-tdd/clojure/10-higher-order-functions-and-composition.md +114 -114
- package/lib/assets/docs/article/getting-start-tdd/clojure/11-persistent-data-and-pipeline.md +138 -138
- package/lib/assets/docs/article/getting-start-tdd/clojure/12-error-handling-and-spec.md +161 -161
- package/lib/assets/docs/article/getting-start-tdd/clojure/index.md +65 -65
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter01.md +232 -232
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter02.md +244 -244
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter03.md +202 -202
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter04.md +92 -92
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter05.md +256 -256
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter06.md +195 -195
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter07.md +214 -214
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter08.md +249 -249
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter09.md +174 -174
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter10.md +166 -166
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter11.md +192 -192
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter12.md +211 -211
- package/lib/assets/docs/article/getting-start-tdd/csharp/index.md +83 -83
- package/lib/assets/docs/article/getting-start-tdd/elixir/01-todo-list-and-first-test.md +87 -87
- package/lib/assets/docs/article/getting-start-tdd/elixir/02-fake-it-and-triangulation.md +95 -95
- package/lib/assets/docs/article/getting-start-tdd/elixir/03-obvious-implementation-and-refactoring.md +109 -109
- package/lib/assets/docs/article/getting-start-tdd/elixir/04-version-control-and-conventional-commits.md +96 -96
- package/lib/assets/docs/article/getting-start-tdd/elixir/05-package-management-and-static-analysis.md +88 -88
- package/lib/assets/docs/article/getting-start-tdd/elixir/06-task-runner-and-ci-cd.md +71 -71
- package/lib/assets/docs/article/getting-start-tdd/elixir/07-structs-and-protocols.md +110 -110
- package/lib/assets/docs/article/getting-start-tdd/elixir/08-pattern-matching-and-guards.md +108 -108
- package/lib/assets/docs/article/getting-start-tdd/elixir/09-module-design-and-behaviours.md +104 -104
- package/lib/assets/docs/article/getting-start-tdd/elixir/10-higher-order-functions-and-pipeline.md +178 -178
- package/lib/assets/docs/article/getting-start-tdd/elixir/11-stream-and-lazy-evaluation.md +142 -142
- package/lib/assets/docs/article/getting-start-tdd/elixir/12-error-handling-and-with.md +145 -145
- package/lib/assets/docs/article/getting-start-tdd/elixir/index.md +35 -35
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter01.md +202 -202
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter02.md +246 -246
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter03.md +218 -218
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter04.md +179 -179
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter05.md +267 -267
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter06.md +190 -190
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter07.md +161 -161
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter08.md +175 -175
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter09.md +222 -222
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter10.md +189 -189
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter11.md +212 -212
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter12.md +215 -215
- package/lib/assets/docs/article/getting-start-tdd/fsharp/index.md +71 -71
- package/lib/assets/docs/article/getting-start-tdd/go/01-todo-list-and-first-test.md +213 -213
- package/lib/assets/docs/article/getting-start-tdd/go/02-fake-it-and-triangulation.md +302 -302
- package/lib/assets/docs/article/getting-start-tdd/go/03-obvious-implementation-and-refactoring.md +339 -339
- package/lib/assets/docs/article/getting-start-tdd/go/04-version-control-and-conventional-commits.md +112 -112
- package/lib/assets/docs/article/getting-start-tdd/go/05-package-management-and-static-analysis.md +272 -272
- package/lib/assets/docs/article/getting-start-tdd/go/06-task-runner-and-ci-cd.md +233 -233
- package/lib/assets/docs/article/getting-start-tdd/go/07-encapsulation-and-polymorphism.md +394 -394
- package/lib/assets/docs/article/getting-start-tdd/go/08-design-patterns.md +422 -422
- package/lib/assets/docs/article/getting-start-tdd/go/09-solid-principles-and-module-design.md +400 -400
- package/lib/assets/docs/article/getting-start-tdd/go/10-higher-order-functions-and-composition.md +226 -226
- package/lib/assets/docs/article/getting-start-tdd/go/11-immutable-data-and-pipeline.md +296 -296
- package/lib/assets/docs/article/getting-start-tdd/go/12-error-handling-and-type-safety.md +411 -411
- package/lib/assets/docs/article/getting-start-tdd/go/index.md +83 -83
- package/lib/assets/docs/article/getting-start-tdd/haskell/01-todo-list-and-first-test.md +279 -279
- package/lib/assets/docs/article/getting-start-tdd/haskell/02-fake-it-and-triangulation.md +337 -337
- package/lib/assets/docs/article/getting-start-tdd/haskell/03-obvious-implementation-and-refactoring.md +257 -257
- package/lib/assets/docs/article/getting-start-tdd/haskell/04-version-control-and-conventional-commits.md +182 -182
- package/lib/assets/docs/article/getting-start-tdd/haskell/05-package-management-and-static-analysis.md +313 -313
- package/lib/assets/docs/article/getting-start-tdd/haskell/06-task-runner-and-ci-cd.md +309 -309
- package/lib/assets/docs/article/getting-start-tdd/haskell/07-algebraic-data-types-and-type-classes.md +412 -412
- package/lib/assets/docs/article/getting-start-tdd/haskell/08-pattern-matching-and-guards.md +390 -390
- package/lib/assets/docs/article/getting-start-tdd/haskell/09-module-design-and-smart-constructors.md +461 -461
- package/lib/assets/docs/article/getting-start-tdd/haskell/10-higher-order-functions-and-currying.md +434 -434
- package/lib/assets/docs/article/getting-start-tdd/haskell/11-function-composition-and-point-free.md +392 -392
- package/lib/assets/docs/article/getting-start-tdd/haskell/12-monad-and-error-handling.md +631 -631
- package/lib/assets/docs/article/getting-start-tdd/haskell/index.md +49 -49
- package/lib/assets/docs/article/getting-start-tdd/index.md +93 -93
- package/lib/assets/docs/article/getting-start-tdd/integration/01-language-overview.md +375 -375
- package/lib/assets/docs/article/getting-start-tdd/integration/02-test-framework-comparison.md +349 -349
- package/lib/assets/docs/article/getting-start-tdd/integration/03-tdd-pattern-comparison.md +445 -445
- package/lib/assets/docs/article/getting-start-tdd/integration/04-type-system-comparison.md +409 -409
- package/lib/assets/docs/article/getting-start-tdd/integration/05-dev-environment-comparison.md +330 -330
- package/lib/assets/docs/article/getting-start-tdd/integration/06-learning-roadmap.md +290 -290
- package/lib/assets/docs/article/getting-start-tdd/integration/index.md +69 -69
- package/lib/assets/docs/article/getting-start-tdd/java/01-todo-list-and-first-test.md +234 -234
- package/lib/assets/docs/article/getting-start-tdd/java/02-fake-it-and-triangulation.md +261 -261
- package/lib/assets/docs/article/getting-start-tdd/java/03-obvious-implementation-and-refactoring.md +185 -185
- package/lib/assets/docs/article/getting-start-tdd/java/04-version-control-and-conventional-commits.md +115 -115
- package/lib/assets/docs/article/getting-start-tdd/java/05-package-management-and-static-analysis.md +382 -382
- package/lib/assets/docs/article/getting-start-tdd/java/06-task-runner-and-ci-cd.md +272 -272
- package/lib/assets/docs/article/getting-start-tdd/java/07-encapsulation-and-polymorphism.md +626 -626
- package/lib/assets/docs/article/getting-start-tdd/java/08-design-patterns.md +393 -393
- package/lib/assets/docs/article/getting-start-tdd/java/09-solid-principles-and-module-design.md +310 -310
- package/lib/assets/docs/article/getting-start-tdd/java/10-higher-order-functions-and-composition.md +188 -188
- package/lib/assets/docs/article/getting-start-tdd/java/11-immutable-data-and-pipeline.md +167 -167
- package/lib/assets/docs/article/getting-start-tdd/java/12-error-handling-and-type-safety.md +205 -205
- package/lib/assets/docs/article/getting-start-tdd/java/index.md +61 -61
- package/lib/assets/docs/article/getting-start-tdd/node/01-todo-list-and-first-test.md +244 -244
- package/lib/assets/docs/article/getting-start-tdd/node/02-fake-it-and-triangulation.md +262 -262
- package/lib/assets/docs/article/getting-start-tdd/node/03-obvious-implementation-and-refactoring.md +169 -169
- package/lib/assets/docs/article/getting-start-tdd/node/04-version-control-and-conventional-commits.md +112 -112
- package/lib/assets/docs/article/getting-start-tdd/node/05-package-management-and-static-analysis.md +314 -314
- package/lib/assets/docs/article/getting-start-tdd/node/06-task-runner-and-ci-cd.md +235 -235
- package/lib/assets/docs/article/getting-start-tdd/node/07-encapsulation-and-polymorphism.md +327 -327
- package/lib/assets/docs/article/getting-start-tdd/node/08-design-patterns.md +322 -322
- package/lib/assets/docs/article/getting-start-tdd/node/09-solid-principles-and-module-design.md +285 -285
- package/lib/assets/docs/article/getting-start-tdd/node/10-higher-order-functions-and-composition.md +199 -199
- package/lib/assets/docs/article/getting-start-tdd/node/11-immutable-data-and-pipeline.md +207 -207
- package/lib/assets/docs/article/getting-start-tdd/node/12-error-handling-and-type-safety.md +295 -295
- package/lib/assets/docs/article/getting-start-tdd/node/index.md +56 -56
- package/lib/assets/docs/article/getting-start-tdd/php/01-todo-list-and-first-test.md +259 -259
- package/lib/assets/docs/article/getting-start-tdd/php/02-fake-it-and-triangulation.md +200 -200
- package/lib/assets/docs/article/getting-start-tdd/php/03-obvious-implementation-and-refactoring.md +248 -248
- package/lib/assets/docs/article/getting-start-tdd/php/04-version-control-and-conventional-commits.md +141 -141
- package/lib/assets/docs/article/getting-start-tdd/php/05-package-management-and-static-analysis.md +410 -410
- package/lib/assets/docs/article/getting-start-tdd/php/06-task-runner-and-ci-cd.md +321 -321
- package/lib/assets/docs/article/getting-start-tdd/php/07-encapsulation-and-polymorphism.md +372 -372
- package/lib/assets/docs/article/getting-start-tdd/php/08-design-patterns.md +453 -453
- package/lib/assets/docs/article/getting-start-tdd/php/09-solid-principles-and-module-design.md +460 -460
- package/lib/assets/docs/article/getting-start-tdd/php/10-higher-order-functions-and-composition.md +182 -182
- package/lib/assets/docs/article/getting-start-tdd/php/11-immutable-data-and-pipeline.md +266 -266
- package/lib/assets/docs/article/getting-start-tdd/php/12-error-handling-and-type-safety.md +308 -308
- package/lib/assets/docs/article/getting-start-tdd/php/index.md +84 -84
- package/lib/assets/docs/article/getting-start-tdd/python/01-todo-list-and-first-test.md +201 -201
- package/lib/assets/docs/article/getting-start-tdd/python/02-fake-it-and-triangulation.md +247 -247
- package/lib/assets/docs/article/getting-start-tdd/python/03-obvious-implementation-and-refactoring.md +199 -199
- package/lib/assets/docs/article/getting-start-tdd/python/04-version-control-and-conventional-commits.md +87 -87
- package/lib/assets/docs/article/getting-start-tdd/python/05-package-management-and-static-analysis.md +274 -274
- package/lib/assets/docs/article/getting-start-tdd/python/06-task-runner-and-ci-cd.md +190 -190
- package/lib/assets/docs/article/getting-start-tdd/python/07-encapsulation-and-polymorphism.md +208 -208
- package/lib/assets/docs/article/getting-start-tdd/python/08-design-patterns.md +172 -172
- package/lib/assets/docs/article/getting-start-tdd/python/09-solid-principles-and-module-design.md +130 -130
- package/lib/assets/docs/article/getting-start-tdd/python/10-higher-order-functions-and-composition.md +122 -122
- package/lib/assets/docs/article/getting-start-tdd/python/11-immutable-data-and-pipeline.md +116 -116
- package/lib/assets/docs/article/getting-start-tdd/python/12-error-handling-and-type-safety.md +126 -126
- package/lib/assets/docs/article/getting-start-tdd/python/index.md +55 -55
- package/lib/assets/docs/article/getting-start-tdd/ruby/01-todo-list-and-first-test.md +231 -231
- package/lib/assets/docs/article/getting-start-tdd/ruby/02-fake-it-and-triangulation.md +238 -238
- package/lib/assets/docs/article/getting-start-tdd/ruby/03-obvious-implementation-and-refactoring.md +228 -228
- package/lib/assets/docs/article/getting-start-tdd/ruby/04-version-control-and-conventional-commits.md +112 -112
- package/lib/assets/docs/article/getting-start-tdd/ruby/05-package-management-and-static-analysis.md +287 -287
- package/lib/assets/docs/article/getting-start-tdd/ruby/06-task-runner-and-ci-cd.md +248 -248
- package/lib/assets/docs/article/getting-start-tdd/ruby/07-encapsulation-and-polymorphism.md +279 -279
- package/lib/assets/docs/article/getting-start-tdd/ruby/08-design-patterns.md +329 -329
- package/lib/assets/docs/article/getting-start-tdd/ruby/09-solid-principles-and-module-design.md +196 -196
- package/lib/assets/docs/article/getting-start-tdd/ruby/10-higher-order-functions-and-composition.md +175 -175
- package/lib/assets/docs/article/getting-start-tdd/ruby/11-immutable-data-and-pipeline.md +237 -237
- package/lib/assets/docs/article/getting-start-tdd/ruby/12-error-handling-and-type-safety.md +398 -398
- package/lib/assets/docs/article/getting-start-tdd/ruby/index.md +83 -83
- package/lib/assets/docs/article/getting-start-tdd/rust/01-todo-list-and-first-test.md +211 -211
- package/lib/assets/docs/article/getting-start-tdd/rust/02-fake-it-and-triangulation.md +264 -264
- package/lib/assets/docs/article/getting-start-tdd/rust/03-obvious-implementation-and-refactoring.md +233 -233
- package/lib/assets/docs/article/getting-start-tdd/rust/04-version-control-and-conventional-commits.md +92 -92
- package/lib/assets/docs/article/getting-start-tdd/rust/05-package-management-and-static-analysis.md +212 -212
- package/lib/assets/docs/article/getting-start-tdd/rust/06-task-runner-and-ci-cd.md +164 -164
- package/lib/assets/docs/article/getting-start-tdd/rust/07-encapsulation-and-polymorphism.md +142 -142
- package/lib/assets/docs/article/getting-start-tdd/rust/08-design-patterns.md +145 -145
- package/lib/assets/docs/article/getting-start-tdd/rust/09-solid-principles-and-module-design.md +110 -110
- package/lib/assets/docs/article/getting-start-tdd/rust/10-higher-order-functions-and-composition.md +94 -94
- package/lib/assets/docs/article/getting-start-tdd/rust/11-immutable-data-and-pipeline.md +105 -105
- package/lib/assets/docs/article/getting-start-tdd/rust/12-error-handling-and-type-safety.md +112 -112
- package/lib/assets/docs/article/getting-start-tdd/rust/index.md +83 -83
- package/lib/assets/docs/article/getting-start-tdd/scala/01-todo-list-and-first-test.md +111 -111
- package/lib/assets/docs/article/getting-start-tdd/scala/02-fake-it-and-triangulation.md +107 -107
- package/lib/assets/docs/article/getting-start-tdd/scala/03-obvious-implementation-and-refactoring.md +99 -99
- package/lib/assets/docs/article/getting-start-tdd/scala/04-version-control-and-conventional-commits.md +123 -123
- package/lib/assets/docs/article/getting-start-tdd/scala/05-package-management-and-static-analysis.md +196 -196
- package/lib/assets/docs/article/getting-start-tdd/scala/06-task-runner-and-ci-cd.md +186 -186
- package/lib/assets/docs/article/getting-start-tdd/scala/07-case-classes-and-traits.md +139 -139
- package/lib/assets/docs/article/getting-start-tdd/scala/08-pattern-matching-and-sealed-traits.md +106 -106
- package/lib/assets/docs/article/getting-start-tdd/scala/09-packages-and-module-design.md +75 -75
- package/lib/assets/docs/article/getting-start-tdd/scala/10-higher-order-functions-and-composition.md +104 -104
- package/lib/assets/docs/article/getting-start-tdd/scala/11-collections-and-lazy-evaluation.md +94 -94
- package/lib/assets/docs/article/getting-start-tdd/scala/12-error-handling-and-type-safety.md +92 -92
- package/lib/assets/docs/article/getting-start-tdd/scala/index.md +65 -65
- package/lib/assets/docs/article/grokking-concurrency/all/index.md +404 -404
- package/lib/assets/docs/article/grokking-concurrency/all/part-1-ch02-sequential.md +554 -554
- package/lib/assets/docs/article/grokking-concurrency/all/part-2-ch04-05-threads.md +469 -469
- package/lib/assets/docs/article/grokking-concurrency/all/part-3-ch06-multitasking.md +520 -520
- package/lib/assets/docs/article/grokking-concurrency/all/part-4-ch07-parallel-patterns.md +420 -420
- package/lib/assets/docs/article/grokking-concurrency/all/part-5-ch08-09-synchronization.md +510 -510
- package/lib/assets/docs/article/grokking-concurrency/all/part-6-ch10-11-nonblocking-io.md +435 -435
- package/lib/assets/docs/article/grokking-concurrency/all/part-7-ch12-async.md +465 -465
- package/lib/assets/docs/article/grokking-concurrency/all/part-8-ch13-mapreduce.md +377 -377
- package/lib/assets/docs/article/grokking-concurrency/clojure/index.md +116 -116
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-1.md +108 -108
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-2.md +101 -101
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-3.md +122 -122
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-4.md +123 -123
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-5.md +118 -118
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-6.md +89 -89
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-7.md +100 -100
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-8.md +120 -120
- package/lib/assets/docs/article/grokking-concurrency/csharp/index.md +101 -101
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-1.md +97 -97
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-2.md +123 -123
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-3.md +101 -101
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-4.md +112 -112
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-5.md +99 -99
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-6.md +61 -61
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-7.md +84 -84
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-8.md +92 -92
- package/lib/assets/docs/article/grokking-concurrency/fsharp/index.md +65 -65
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-1.md +80 -80
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-2.md +103 -103
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-3.md +94 -94
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-4.md +110 -110
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-5.md +104 -104
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-6.md +93 -93
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-7.md +121 -121
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-8.md +107 -107
- package/lib/assets/docs/article/grokking-concurrency/haskell/index.md +248 -248
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-1.md +96 -96
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-2.md +96 -96
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-3.md +91 -91
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-4.md +106 -106
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-5.md +99 -99
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-6.md +95 -95
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-7.md +111 -111
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-8.md +118 -118
- package/lib/assets/docs/article/grokking-concurrency/index.md +66 -66
- package/lib/assets/docs/article/grokking-concurrency/java/index.md +102 -102
- package/lib/assets/docs/article/grokking-concurrency/java/part-1.md +308 -308
- package/lib/assets/docs/article/grokking-concurrency/java/part-2.md +334 -334
- package/lib/assets/docs/article/grokking-concurrency/java/part-3.md +221 -221
- package/lib/assets/docs/article/grokking-concurrency/java/part-4.md +213 -213
- package/lib/assets/docs/article/grokking-concurrency/java/part-5.md +112 -112
- package/lib/assets/docs/article/grokking-concurrency/java/part-6.md +69 -69
- package/lib/assets/docs/article/grokking-concurrency/java/part-7.md +101 -101
- package/lib/assets/docs/article/grokking-concurrency/java/part-8.md +101 -101
- package/lib/assets/docs/article/grokking-concurrency/python/index.md +313 -313
- package/lib/assets/docs/article/grokking-concurrency/python/part-1.md +239 -239
- package/lib/assets/docs/article/grokking-concurrency/python/part-2.md +418 -418
- package/lib/assets/docs/article/grokking-concurrency/python/part-3.md +227 -227
- package/lib/assets/docs/article/grokking-concurrency/python/part-4.md +299 -299
- package/lib/assets/docs/article/grokking-concurrency/python/part-5.md +315 -315
- package/lib/assets/docs/article/grokking-concurrency/python/part-6.md +297 -297
- package/lib/assets/docs/article/grokking-concurrency/python/part-7.md +314 -314
- package/lib/assets/docs/article/grokking-concurrency/python/part-8.md +360 -360
- package/lib/assets/docs/article/grokking-concurrency/rust/index.md +270 -270
- package/lib/assets/docs/article/grokking-concurrency/rust/part-1.md +108 -108
- package/lib/assets/docs/article/grokking-concurrency/rust/part-2.md +120 -120
- package/lib/assets/docs/article/grokking-concurrency/rust/part-3.md +126 -126
- package/lib/assets/docs/article/grokking-concurrency/rust/part-4.md +175 -175
- package/lib/assets/docs/article/grokking-concurrency/rust/part-5.md +158 -158
- package/lib/assets/docs/article/grokking-concurrency/rust/part-6.md +94 -94
- package/lib/assets/docs/article/grokking-concurrency/rust/part-7.md +133 -133
- package/lib/assets/docs/article/grokking-concurrency/rust/part-8.md +155 -155
- package/lib/assets/docs/article/grokking-concurrency/scala/index.md +69 -69
- package/lib/assets/docs/article/grokking-concurrency/scala/part-1.md +78 -78
- package/lib/assets/docs/article/grokking-concurrency/scala/part-2.md +112 -112
- package/lib/assets/docs/article/grokking-concurrency/scala/part-3.md +93 -93
- package/lib/assets/docs/article/grokking-concurrency/scala/part-4.md +110 -110
- package/lib/assets/docs/article/grokking-concurrency/scala/part-5.md +119 -119
- package/lib/assets/docs/article/grokking-concurrency/scala/part-6.md +83 -83
- package/lib/assets/docs/article/grokking-concurrency/scala/part-7.md +131 -131
- package/lib/assets/docs/article/grokking-concurrency/scala/part-8.md +129 -129
- package/lib/assets/docs/article/grokkingfp/all/index.md +368 -368
- package/lib/assets/docs/article/grokkingfp/all/part-1-ch01-fp-introduction.md +530 -530
- package/lib/assets/docs/article/grokkingfp/all/part-1-ch02-pure-functions.md +923 -923
- package/lib/assets/docs/article/grokkingfp/all/part-2-ch03-immutable-data.md +1128 -1128
- package/lib/assets/docs/article/grokkingfp/all/part-2-ch04-higher-order-functions.md +1104 -1104
- package/lib/assets/docs/article/grokkingfp/all/part-2-ch05-flatmap.md +1026 -1026
- package/lib/assets/docs/article/grokkingfp/all/part-3-ch06-option.md +785 -785
- package/lib/assets/docs/article/grokkingfp/all/part-3-ch07-either-adt.md +871 -871
- package/lib/assets/docs/article/grokkingfp/all/part-4-ch08-io-monad.md +972 -972
- package/lib/assets/docs/article/grokkingfp/all/part-4-ch09-streams.md +926 -926
- package/lib/assets/docs/article/grokkingfp/all/part-5-ch10-concurrency.md +870 -870
- package/lib/assets/docs/article/grokkingfp/all/part-6-ch11-application.md +715 -715
- package/lib/assets/docs/article/grokkingfp/all/part-6-ch12-testing.md +626 -626
- package/lib/assets/docs/article/grokkingfp/all/writing-plan.md +712 -712
- package/lib/assets/docs/article/grokkingfp/clojure/index.md +276 -276
- package/lib/assets/docs/article/grokkingfp/clojure/part-1.md +667 -667
- package/lib/assets/docs/article/grokkingfp/clojure/part-2.md +643 -643
- package/lib/assets/docs/article/grokkingfp/clojure/part-3.md +620 -620
- package/lib/assets/docs/article/grokkingfp/clojure/part-4.md +697 -697
- package/lib/assets/docs/article/grokkingfp/clojure/part-5.md +751 -751
- package/lib/assets/docs/article/grokkingfp/clojure/part-6.md +721 -721
- package/lib/assets/docs/article/grokkingfp/csharp/index.md +246 -246
- package/lib/assets/docs/article/grokkingfp/csharp/part-1.md +811 -811
- package/lib/assets/docs/article/grokkingfp/csharp/part-2.md +971 -971
- package/lib/assets/docs/article/grokkingfp/csharp/part-3.md +981 -981
- package/lib/assets/docs/article/grokkingfp/csharp/part-4.md +949 -949
- package/lib/assets/docs/article/grokkingfp/csharp/part-5.md +947 -947
- package/lib/assets/docs/article/grokkingfp/csharp/part-6.md +739 -739
- package/lib/assets/docs/article/grokkingfp/elixir/index.md +203 -203
- package/lib/assets/docs/article/grokkingfp/elixir/part-1.md +712 -712
- package/lib/assets/docs/article/grokkingfp/elixir/part-2.md +838 -838
- package/lib/assets/docs/article/grokkingfp/elixir/part-3.md +985 -985
- package/lib/assets/docs/article/grokkingfp/elixir/part-4.md +974 -974
- package/lib/assets/docs/article/grokkingfp/elixir/part-5.md +1286 -1286
- package/lib/assets/docs/article/grokkingfp/elixir/part-6.md +1049 -1049
- package/lib/assets/docs/article/grokkingfp/fsharp/index.md +210 -210
- package/lib/assets/docs/article/grokkingfp/fsharp/part-1.md +714 -714
- package/lib/assets/docs/article/grokkingfp/fsharp/part-2.md +961 -961
- package/lib/assets/docs/article/grokkingfp/fsharp/part-3.md +972 -972
- package/lib/assets/docs/article/grokkingfp/fsharp/part-4.md +832 -832
- package/lib/assets/docs/article/grokkingfp/fsharp/part-5.md +911 -911
- package/lib/assets/docs/article/grokkingfp/fsharp/part-6.md +922 -922
- package/lib/assets/docs/article/grokkingfp/haskell/index.md +234 -234
- package/lib/assets/docs/article/grokkingfp/haskell/part-1.md +591 -591
- package/lib/assets/docs/article/grokkingfp/haskell/part-2.md +866 -866
- package/lib/assets/docs/article/grokkingfp/haskell/part-3.md +915 -915
- package/lib/assets/docs/article/grokkingfp/haskell/part-4.md +878 -878
- package/lib/assets/docs/article/grokkingfp/haskell/part-5.md +845 -845
- package/lib/assets/docs/article/grokkingfp/haskell/part-6.md +844 -844
- package/lib/assets/docs/article/grokkingfp/index.md +143 -143
- package/lib/assets/docs/article/grokkingfp/java/index.md +211 -211
- package/lib/assets/docs/article/grokkingfp/java/part-1.md +648 -648
- package/lib/assets/docs/article/grokkingfp/java/part-2.md +675 -675
- package/lib/assets/docs/article/grokkingfp/java/part-3.md +672 -672
- package/lib/assets/docs/article/grokkingfp/java/part-4.md +771 -771
- package/lib/assets/docs/article/grokkingfp/java/part-5.md +959 -959
- package/lib/assets/docs/article/grokkingfp/java/part-6.md +1328 -1328
- package/lib/assets/docs/article/grokkingfp/python/index.md +258 -258
- package/lib/assets/docs/article/grokkingfp/python/part-1.md +443 -443
- package/lib/assets/docs/article/grokkingfp/python/part-2.md +958 -958
- package/lib/assets/docs/article/grokkingfp/python/part-3.md +1004 -1004
- package/lib/assets/docs/article/grokkingfp/python/part-4.md +765 -765
- package/lib/assets/docs/article/grokkingfp/python/part-5.md +747 -747
- package/lib/assets/docs/article/grokkingfp/python/part-6.md +861 -861
- package/lib/assets/docs/article/grokkingfp/ruby/index.md +330 -330
- package/lib/assets/docs/article/grokkingfp/ruby/part-1.md +755 -755
- package/lib/assets/docs/article/grokkingfp/ruby/part-2.md +938 -938
- package/lib/assets/docs/article/grokkingfp/ruby/part-3.md +946 -946
- package/lib/assets/docs/article/grokkingfp/ruby/part-4.md +921 -921
- package/lib/assets/docs/article/grokkingfp/ruby/part-5.md +908 -908
- package/lib/assets/docs/article/grokkingfp/ruby/part-6.md +1412 -1412
- package/lib/assets/docs/article/grokkingfp/rust/index.md +242 -242
- package/lib/assets/docs/article/grokkingfp/rust/part-1.md +634 -634
- package/lib/assets/docs/article/grokkingfp/rust/part-2.md +1060 -1060
- package/lib/assets/docs/article/grokkingfp/rust/part-3.md +994 -994
- package/lib/assets/docs/article/grokkingfp/rust/part-4.md +573 -573
- package/lib/assets/docs/article/grokkingfp/rust/part-5.md +705 -705
- package/lib/assets/docs/article/grokkingfp/rust/part-6.md +508 -508
- package/lib/assets/docs/article/grokkingfp/scala/index.md +171 -171
- package/lib/assets/docs/article/grokkingfp/scala/part-1.md +543 -543
- package/lib/assets/docs/article/grokkingfp/scala/part-2.md +946 -946
- package/lib/assets/docs/article/grokkingfp/scala/part-3.md +919 -919
- package/lib/assets/docs/article/grokkingfp/scala/part-4.md +742 -742
- package/lib/assets/docs/article/grokkingfp/scala/part-5.md +722 -722
- package/lib/assets/docs/article/grokkingfp/scala/part-6.md +867 -867
- package/lib/assets/docs/article/grokkingfp/typescript/index.md +273 -273
- package/lib/assets/docs/article/grokkingfp/typescript/part-1.md +561 -561
- package/lib/assets/docs/article/grokkingfp/typescript/part-2.md +1129 -1129
- package/lib/assets/docs/article/grokkingfp/typescript/part-3.md +842 -842
- package/lib/assets/docs/article/grokkingfp/typescript/part-4.md +1087 -1087
- package/lib/assets/docs/article/grokkingfp/typescript/part-5.md +717 -717
- package/lib/assets/docs/article/grokkingfp/typescript/part-6.md +982 -982
- package/lib/assets/docs/article/practical-database-design/index.md +121 -121
- package/lib/assets/docs/article/practical-database-design/part1/chapter01.md +288 -288
- package/lib/assets/docs/article/practical-database-design/part1/chapter02.md +518 -518
- package/lib/assets/docs/article/practical-database-design/part1/chapter03.md +557 -557
- package/lib/assets/docs/article/practical-database-design/part2/chapter04.md +924 -924
- package/lib/assets/docs/article/practical-database-design/part2/chapter05.md +1627 -1627
- package/lib/assets/docs/article/practical-database-design/part2/chapter06.md +2716 -2716
- package/lib/assets/docs/article/practical-database-design/part2/chapter07.md +2082 -2082
- package/lib/assets/docs/article/practical-database-design/part2/chapter08.md +2105 -2105
- package/lib/assets/docs/article/practical-database-design/part2/chapter09.md +2031 -2031
- package/lib/assets/docs/article/practical-database-design/part2/chapter10.md +1387 -1387
- package/lib/assets/docs/article/practical-database-design/part2/chapter11.md +1677 -1677
- package/lib/assets/docs/article/practical-database-design/part2/chapter12.md +1417 -1417
- package/lib/assets/docs/article/practical-database-design/part2/chapter13.md +1434 -1434
- package/lib/assets/docs/article/practical-database-design/part3/chapter14.md +667 -667
- package/lib/assets/docs/article/practical-database-design/part3/chapter15.md +1625 -1625
- package/lib/assets/docs/article/practical-database-design/part3/chapter16.md +1915 -1915
- package/lib/assets/docs/article/practical-database-design/part3/chapter17.md +1708 -1708
- package/lib/assets/docs/article/practical-database-design/part3/chapter18.md +2095 -2095
- package/lib/assets/docs/article/practical-database-design/part3/chapter19.md +1123 -1123
- package/lib/assets/docs/article/practical-database-design/part3/chapter20.md +1031 -1031
- package/lib/assets/docs/article/practical-database-design/part3/chapter21.md +1382 -1382
- package/lib/assets/docs/article/practical-database-design/part3-orm/chapter14-orm.md +991 -991
- package/lib/assets/docs/article/practical-database-design/part3-orm/chapter15-orm.md +1300 -1300
- package/lib/assets/docs/article/practical-database-design/part3-orm/chapter16-orm.md +1166 -1166
- package/lib/assets/docs/article/practical-database-design/part3-orm/chapter17-orm.md +1584 -1584
- package/lib/assets/docs/article/practical-database-design/part3-orm/chapter18-orm.md +1183 -1183
- package/lib/assets/docs/article/practical-database-design/part3-orm/chapter19-orm.md +1016 -1016
- package/lib/assets/docs/article/practical-database-design/part3-orm/chapter20-orm.md +1753 -1753
- package/lib/assets/docs/article/practical-database-design/part3-orm/chapter21-orm.md +1447 -1447
- package/lib/assets/docs/article/practical-database-design/part3-orm/chapter22-orm.md +1878 -1878
- package/lib/assets/docs/article/practical-database-design/part4/chapter22.md +965 -965
- package/lib/assets/docs/article/practical-database-design/part4/chapter23.md +2069 -2069
- package/lib/assets/docs/article/practical-database-design/part4/chapter24.md +2439 -2439
- package/lib/assets/docs/article/practical-database-design/part4/chapter25.md +3661 -3661
- package/lib/assets/docs/article/practical-database-design/part4/chapter26.md +2916 -2916
- package/lib/assets/docs/article/practical-database-design/part4/chapter27.md +3105 -3105
- package/lib/assets/docs/article/practical-database-design/part4/chapter28.md +2697 -2697
- package/lib/assets/docs/article/practical-database-design/part4/chapter29.md +2544 -2544
- package/lib/assets/docs/article/practical-database-design/part4/chapter30.md +2180 -2180
- package/lib/assets/docs/article/practical-database-design/part4/chapter31.md +1192 -1192
- package/lib/assets/docs/article/practical-database-design/part4/chapter32.md +2101 -2101
- package/lib/assets/docs/article/practical-database-design/part5/chapter33.md +1032 -1032
- package/lib/assets/docs/article/practical-database-design/part5/chapter34.md +1609 -1609
- package/lib/assets/docs/article/practical-database-design/part5/chapter35.md +1453 -1453
- package/lib/assets/docs/article/practical-database-design/part5/chapter36.md +1292 -1292
- package/lib/assets/docs/article/practical-database-design/part5/chapter37.md +1470 -1470
- package/lib/assets/docs/article/practical-database-design/part5/chapter38.md +1698 -1698
- package/lib/assets/docs/article/practical-database-design/part5/chapter39.md +2334 -2334
- package/lib/assets/docs/article/practical-database-design/study/study2-1.md +1693 -1693
- package/lib/assets/docs/article/practical-database-design/study/study2-2.md +1347 -1347
- package/lib/assets/docs/article/practical-database-design/study/study2-3.md +2044 -2044
- package/lib/assets/docs/article/practical-database-design/study/study2-4.md +2229 -2229
- package/lib/assets/docs/article/practical-database-design/study/study2-5.md +2418 -2418
- package/lib/assets/docs/article/practical-database-design/study/study3-1.md +2205 -2205
- package/lib/assets/docs/article/practical-database-design/study/study3-2.md +2221 -2221
- package/lib/assets/docs/article/practical-database-design/study/study3-3.md +2253 -2253
- package/lib/assets/docs/article/practical-database-design/study/study3-4.md +2106 -2106
- package/lib/assets/docs/article/practical-database-design/study/study3-5.md +2507 -2507
- package/lib/assets/docs/article/practical-database-design/study/study4-1.md +2587 -2587
- package/lib/assets/docs/article/practical-database-design/study/study4-2.md +2075 -2075
- package/lib/assets/docs/article/practical-database-design/study/study4-3.md +1805 -1805
- package/lib/assets/docs/article/practical-database-design/study/study4-4.md +1895 -1895
- package/lib/assets/docs/article/practical-database-design/study/study4-5.md +2878 -2878
- package/lib/assets/docs/assets/css/extra.css +29 -29
- package/lib/assets/docs/assets/js/extra.js +44 -44
- package/lib/assets/docs/development/index.md +39 -39
- package/lib/assets/docs/operation/index.md +11 -11
- package/lib/assets/docs/reference/CodexCLIMCP/343/202/242/343/203/227/343/203/252/343/202/261/343/203/274/343/202/267/343/203/247/343/203/263/351/226/213/347/231/272/343/203/225/343/203/255/343/203/274.md +532 -532
- package/lib/assets/docs/reference/CodexCLIMCP/343/202/265/343/203/274/343/203/220/343/203/274/350/250/255/345/256/232/346/211/213/351/240/206.md +341 -341
- package/lib/assets/docs/reference/Java/343/202/242/343/203/227/343/203/252/343/202/261/343/203/274/343/202/267/343/203/247/343/203/263/347/222/260/345/242/203/346/247/213/347/257/211/343/202/254/343/202/244/343/203/211.md +581 -580
- package/lib/assets/docs/reference/SonarQube/343/203/255/343/203/274/343/202/253/343/203/253/347/222/260/345/242/203/343/202/273/343/203/203/343/203/210/343/202/242/343/203/203/343/203/227/346/211/213/351/240/206/346/233/270.md +642 -642
- package/lib/assets/docs/reference/TypeScript/343/202/242/343/203/227/343/203/252/343/202/261/343/203/274/343/202/267/343/203/247/343/203/263/347/222/260/345/242/203/346/247/213/347/257/211/343/202/254/343/202/244/343/203/211.md +465 -465
- package/lib/assets/docs/reference/UI/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +450 -450
- package/lib/assets/docs/reference/images/Ansoff.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/BrandBasicStrategy.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/BrandCategorization.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/BrandRecurutementStrategy.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/BrandValue.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/BusinessActivitiy.svg +3 -3
- package/lib/assets/docs/reference/images/HRM.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/MarketingStructure.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/OrganizationElemnts.svg +3 -3
- package/lib/assets/docs/reference/images/PPM.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/PositioningMap.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/ProductLayer.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/ProductMix.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/SWOT.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/TargetMarket.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/ThreeGenericStrategies.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/VRIO.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/ValueChain.drawio.svg +3 -3
- package/lib/assets/docs/reference/index.md +52 -52
- package/lib/assets/docs/reference//343/202/210/343/201/204/343/202/275/343/203/225/343/203/210/343/202/246/343/202/247/343/202/242/343/201/250/343/201/257.md +250 -242
- package/lib/assets/docs/reference//343/202/242/343/203/274/343/202/255/343/203/206/343/202/257/343/203/201/343/203/243/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +2216 -2216
- package/lib/assets/docs/reference//343/202/244/343/203/263/343/203/225/343/203/251/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +1878 -1878
- package/lib/assets/docs/reference//343/202/250/343/202/257/343/202/271/343/203/210/343/203/252/343/203/274/343/203/240/343/203/227/343/203/255/343/202/260/343/203/251/343/203/237/343/203/263/343/202/260.md +550 -544
- package/lib/assets/docs/reference//343/202/263/343/203/274/343/203/207/343/202/243/343/203/263/343/202/260/343/201/250/343/203/206/343/202/271/343/203/210/343/202/254/343/202/244/343/203/211.md +705 -705
- package/lib/assets/docs/reference//343/203/206/343/202/271/343/203/210/346/210/246/347/225/245/343/202/254/343/202/244/343/203/211.md +1313 -1313
- package/lib/assets/docs/reference//343/203/207/343/203/274/343/202/277/343/203/242/343/203/207/343/203/253/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +311 -311
- package/lib/assets/docs/reference//343/203/211/343/203/241/343/202/244/343/203/263/343/203/242/343/203/207/343/203/253/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +599 -599
- package/lib/assets/docs/reference//343/203/223/343/202/270/343/203/215/343/202/271/343/202/242/343/203/274/343/202/255/343/203/206/343/202/257/343/203/201/343/203/243/345/210/206/346/236/220/343/202/254/343/202/244/343/203/211.md +528 -528
- package/lib/assets/docs/reference//343/203/246/343/203/274/343/202/271/343/202/261/343/203/274/343/202/271/344/275/234/346/210/220/343/202/254/343/202/244/343/203/211.md +689 -682
- package/lib/assets/docs/reference//343/203/252/343/203/252/343/203/274/343/202/271/343/202/254/343/202/244/343/203/211.md +461 -461
- package/lib/assets/docs/reference//343/203/252/343/203/252/343/203/274/343/202/271/343/203/273/343/202/244/343/203/206/343/203/254/343/203/274/343/202/267/343/203/247/343/203/263/350/250/210/347/224/273/343/202/254/343/202/244/343/203/211.md +580 -560
- package/lib/assets/docs/reference//343/203/255/343/202/270/343/202/253/343/203/253/343/202/267/343/203/263/343/202/255/343/203/263/343/202/260.md +1367 -1367
- package/lib/assets/docs/reference//344/274/201/346/245/255/347/265/214/345/226/266/350/253/226.md +2637 -2636
- package/lib/assets/docs/reference//347/222/260/345/242/203/345/244/211/346/225/260/347/256/241/347/220/206/343/202/254/343/202/244/343/203/211.md +665 -663
- package/lib/assets/docs/reference//350/246/201/344/273/266/345/256/232/347/276/251/343/202/254/343/202/244/343/203/211.md +1248 -1248
- package/lib/assets/docs/reference//350/250/200/350/252/236/345/210/245/351/226/213/347/231/272/343/202/254/343/202/244/343/203/211.md +28 -0
- package/lib/assets/docs/reference//351/201/213/345/226/266/347/256/241/347/220/206.md +1482 -1482
- package/lib/assets/docs/reference//351/201/213/347/224/250/343/202/271/343/202/257/343/203/252/343/203/227/343/203/210/344/275/234/346/210/220/343/202/254/343/202/244/343/203/211.md +421 -421
- package/lib/assets/docs/reference//351/201/213/347/224/250/350/246/201/344/273/266/345/256/232/347/276/251/343/202/254/343/202/244/343/203/211.md +392 -392
- package/lib/assets/docs/reference//351/226/213/347/231/272/343/202/254/343/202/244/343/203/211.md +299 -299
- package/lib/assets/docs/reference//351/235/236/346/251/237/350/203/275/350/246/201/344/273/266/345/256/232/347/276/251/343/202/254/343/202/244/343/203/211.md +1236 -1236
- package/lib/assets/docs/review/index.md +5 -5
- package/lib/assets/docs/strategy/index.md +1 -1
- package/lib/assets/docs/template/ADR.md +30 -30
- package/lib/assets/docs/template/AWS/343/202/271/343/203/206/343/203/274/343/202/270/343/203/263/343/202/260/347/222/260/345/242/203/343/202/273/343/203/203/343/203/210/343/202/242/343/203/203/343/203/227/346/211/213/351/240/206/346/233/270.md +1366 -1366
- package/lib/assets/docs/template/AWS/343/203/227/343/203/255/343/203/200/343/202/257/343/202/267/343/203/247/343/203/263/347/222/260/345/242/203/343/202/273/343/203/203/343/203/210/343/202/242/343/203/203/343/203/227/346/211/213/351/240/206/346/233/270.md +634 -634
- package/lib/assets/docs/template/README.md +50 -50
- package/lib/assets/docs/template/index.md +23 -23
- package/lib/assets/docs/template//343/201/276/343/201/232/343/201/223/343/202/214/343/202/222/350/252/255/343/202/202/343/201/206/343/203/252/343/202/271/343/203/210.md +12 -12
- package/lib/assets/docs/template//343/202/242/343/203/227/343/203/252/343/202/261/343/203/274/343/202/267/343/203/247/343/203/263/351/226/213/347/231/272/347/222/260/345/242/203/343/202/273/343/203/203/343/203/210/343/202/242/343/203/203/343/203/227/346/211/213/351/240/206/346/233/270.md +547 -547
- package/lib/assets/docs/template//343/202/244/343/203/206/343/203/254/343/203/274/343/202/267/343/203/247/343/203/263/345/256/214/344/272/206/345/240/261/345/221/212/346/233/270.md +58 -58
- package/lib/assets/docs/template//343/202/244/343/203/263/343/202/273/343/203/227/343/202/267/343/203/247/343/203/263/343/203/207/343/203/203/343/202/255.md +13 -13
- package/lib/assets/docs/template//343/203/223/343/202/270/343/203/215/343/202/271/343/202/242/343/203/274/343/202/255/343/203/206/343/202/257/343/203/201/343/203/243.md +379 -379
- package/lib/assets/docs/template//344/274/201/346/245/255/345/210/206/346/236/220.md +573 -573
- package/lib/assets/docs/template//345/256/214/345/205/250/345/275/242/345/274/217/343/201/256/343/203/246/343/203/274/343/202/271/343/202/261/343/203/274/343/202/271.md +69 -68
- package/lib/assets/docs/template//350/246/201/344/273/266/345/256/232/347/276/251.md +669 -669
- package/lib/assets/docs/template//350/250/255/350/250/210.md +173 -173
- package/lib/assets/docs/template//351/226/213/347/231/272/347/222/260/345/242/203/343/202/273/343/203/203/343/203/210/343/202/242/343/203/203/343/203/227/346/211/213/351/240/206/346/233/270.md +688 -688
- package/lib/assets/gulpfile.js +25 -25
- package/lib/assets/mkdocs.yml +136 -135
- package/lib/assets/ops/docker/mkdoc/Dockerfile +19 -19
- package/lib/assets/ops/scripts/journal.js +180 -180
- package/lib/assets/ops/scripts/mkdocs.js +82 -82
- package/lib/assets/ops/scripts/release.js +431 -431
- package/lib/assets/ops/scripts/sonar_local.js +726 -726
- package/lib/assets/ops/scripts/ssh.js +190 -190
- package/lib/assets/ops/scripts/vault.js +299 -299
- package/lib/assets/package-lock.json +1653 -1653
- package/lib/assets/package.json +40 -40
- package/lib/gulpfile.js +37 -37
- package/package.json +41 -41
- package/lib/assets/.claude/agent-memory/xp-programmer/MEMORY.md +0 -6
- package/lib/assets/.claude/agent-memory/xp-programmer/project_cargo_tracker.md +0 -11
- package/lib/assets/.claude/agent-memory/xp-programmer/project_ddd_patterns.md +0 -27
- package/lib/assets/.claude/agent-memory/xp-programmer/project_us07_route_assignment.md +0 -19
|
@@ -1,739 +1,739 @@
|
|
|
1
|
-
# Part VI: 実践的なアプリケーション構築とテスト
|
|
2
|
-
|
|
3
|
-
本章では、これまで学んだ関数型プログラミングの概念を統合し、実践的なアプリケーションを構築します。また、関数型プログラミングにおけるテスト戦略についても学びます。
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## 第11章: 実践的なアプリケーション構築
|
|
8
|
-
|
|
9
|
-
### 11.1 TravelGuide アプリケーション
|
|
10
|
-
|
|
11
|
-
旅行ガイドアプリケーションを例に、実践的な FP アプリケーションの構築方法を学びます。
|
|
12
|
-
|
|
13
|
-
```plantuml
|
|
14
|
-
@startuml
|
|
15
|
-
!theme plain
|
|
16
|
-
|
|
17
|
-
package "TravelGuide Application" {
|
|
18
|
-
rectangle "Model" {
|
|
19
|
-
class Location {
|
|
20
|
-
Id: LocationId
|
|
21
|
-
Name: string
|
|
22
|
-
Population: int
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
class Attraction {
|
|
26
|
-
Name: string
|
|
27
|
-
Description: Option<string>
|
|
28
|
-
Location: Location
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
class TravelGuide {
|
|
32
|
-
Attraction: Attraction
|
|
33
|
-
Subjects: Seq<string>
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
rectangle "Data Access" {
|
|
38
|
-
interface IDataAccess {
|
|
39
|
-
+FindAttractions(): Task<Seq<Attraction>>
|
|
40
|
-
+FindArtistsFromLocation(): Task<Seq<MusicArtist>>
|
|
41
|
-
+FindMoviesAboutLocation(): Task<Seq<Movie>>
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
class TestDataAccess
|
|
45
|
-
class CachedDataAccess
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
IDataAccess <|.. TestDataAccess
|
|
49
|
-
IDataAccess <|.. CachedDataAccess
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
@enduml
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
### 11.2 ドメインモデルの定義
|
|
56
|
-
|
|
57
|
-
**ソースファイル**: `app/csharp/src/Ch11/TravelGuide.cs`
|
|
58
|
-
|
|
59
|
-
```csharp
|
|
60
|
-
/// <summary>
|
|
61
|
-
/// ロケーション ID(値オブジェクト)
|
|
62
|
-
/// </summary>
|
|
63
|
-
public readonly record struct LocationId(string Value)
|
|
64
|
-
{
|
|
65
|
-
public static LocationId Create(string id) => new(id);
|
|
66
|
-
public override string ToString() => Value;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/// <summary>
|
|
70
|
-
/// ロケーション
|
|
71
|
-
/// </summary>
|
|
72
|
-
public record Location(LocationId Id, string Name, int Population);
|
|
73
|
-
|
|
74
|
-
/// <summary>
|
|
75
|
-
/// アトラクション(観光地)
|
|
76
|
-
/// </summary>
|
|
77
|
-
public record Attraction(string Name, Option<string> Description, Location Location);
|
|
78
|
-
|
|
79
|
-
/// <summary>
|
|
80
|
-
/// ミュージックアーティスト
|
|
81
|
-
/// </summary>
|
|
82
|
-
public record MusicArtist(string Name, Option<string> Genre);
|
|
83
|
-
|
|
84
|
-
/// <summary>
|
|
85
|
-
/// 映画
|
|
86
|
-
/// </summary>
|
|
87
|
-
public record Movie(string Name, Option<int> BoxOffice);
|
|
88
|
-
|
|
89
|
-
/// <summary>
|
|
90
|
-
/// 旅行ガイド
|
|
91
|
-
/// </summary>
|
|
92
|
-
public record TravelGuide(Attraction Attraction, Seq<string> Subjects);
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
### 11.3 データアクセス層の抽象化
|
|
96
|
-
|
|
97
|
-
外部データソースへのアクセスをインターフェースで抽象化します。
|
|
98
|
-
|
|
99
|
-
```csharp
|
|
100
|
-
/// <summary>
|
|
101
|
-
/// データアクセスインターフェース
|
|
102
|
-
/// </summary>
|
|
103
|
-
public interface IDataAccess
|
|
104
|
-
{
|
|
105
|
-
Task<Seq<Attraction>> FindAttractions(string name, AttractionOrdering ordering, int limit);
|
|
106
|
-
Task<Seq<MusicArtist>> FindArtistsFromLocation(LocationId locationId, int limit);
|
|
107
|
-
Task<Seq<Movie>> FindMoviesAboutLocation(LocationId locationId, int limit);
|
|
108
|
-
}
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
```plantuml
|
|
112
|
-
@startuml
|
|
113
|
-
!theme plain
|
|
114
|
-
|
|
115
|
-
interface IDataAccess {
|
|
116
|
-
+FindAttractions(): Task<Seq<Attraction>>
|
|
117
|
-
+FindArtistsFromLocation(): Task<Seq<MusicArtist>>
|
|
118
|
-
+FindMoviesAboutLocation(): Task<Seq<Movie>>
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
note right of IDataAccess
|
|
122
|
-
純粋な関数型インターフェース
|
|
123
|
-
すべての操作は Task でラップ
|
|
124
|
-
end note
|
|
125
|
-
|
|
126
|
-
class TestDataAccess {
|
|
127
|
-
-testAttractions: Seq<Attraction>
|
|
128
|
-
+FindAttractions(): Task<Seq<Attraction>>
|
|
129
|
-
+FindArtistsFromLocation(): Task<Seq<MusicArtist>>
|
|
130
|
-
+FindMoviesAboutLocation(): Task<Seq<Movie>>
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
class CachedDataAccess {
|
|
134
|
-
-cache: Ref<Map<string, T>>
|
|
135
|
-
+FindAttractions(): Task<Seq<Attraction>>
|
|
136
|
-
+FindArtistsFromLocation(): Task<Seq<MusicArtist>>
|
|
137
|
-
+FindMoviesAboutLocation(): Task<Seq<Movie>>
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
IDataAccess <|.. TestDataAccess
|
|
141
|
-
IDataAccess <|.. CachedDataAccess
|
|
142
|
-
|
|
143
|
-
@enduml
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
### 11.4 Resource によるリソース管理
|
|
147
|
-
|
|
148
|
-
C# では `IDisposable` パターンと `using` ステートメントでリソースを管理できますが、より関数型的な `Resource<T>` 型を実装することもできます。
|
|
149
|
-
|
|
150
|
-
```csharp
|
|
151
|
-
/// <summary>
|
|
152
|
-
/// リソースを安全に管理するための型
|
|
153
|
-
/// </summary>
|
|
154
|
-
public class Resource<T>
|
|
155
|
-
{
|
|
156
|
-
private readonly Func<Task<T>> _acquire;
|
|
157
|
-
private readonly Func<T, Task> _release;
|
|
158
|
-
|
|
159
|
-
private Resource(Func<Task<T>> acquire, Func<T, Task> release)
|
|
160
|
-
{
|
|
161
|
-
_acquire = acquire;
|
|
162
|
-
_release = release;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/// <summary>
|
|
166
|
-
/// リソースを作成
|
|
167
|
-
/// </summary>
|
|
168
|
-
public static Resource<T> Make(Func<Task<T>> acquire, Func<T, Task> release)
|
|
169
|
-
=> new(acquire, release);
|
|
170
|
-
|
|
171
|
-
/// <summary>
|
|
172
|
-
/// リソースを使用して操作を実行
|
|
173
|
-
/// </summary>
|
|
174
|
-
public async Task<TResult> UseAsync<TResult>(Func<T, Task<TResult>> f)
|
|
175
|
-
{
|
|
176
|
-
var resource = await _acquire();
|
|
177
|
-
try
|
|
178
|
-
{
|
|
179
|
-
return await f(resource);
|
|
180
|
-
}
|
|
181
|
-
finally
|
|
182
|
-
{
|
|
183
|
-
await _release(resource);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
```plantuml
|
|
190
|
-
@startuml
|
|
191
|
-
!theme plain
|
|
192
|
-
|
|
193
|
-
participant "Application" as app
|
|
194
|
-
participant "Resource" as res
|
|
195
|
-
participant "External Resource" as ext
|
|
196
|
-
|
|
197
|
-
app -> res: Resource.Make(acquire, release)
|
|
198
|
-
res -> ext: acquire
|
|
199
|
-
ext --> res: resource
|
|
200
|
-
|
|
201
|
-
app -> res: UseAsync(operation)
|
|
202
|
-
res -> ext: execute operation
|
|
203
|
-
ext --> res: result
|
|
204
|
-
res --> app: result
|
|
205
|
-
|
|
206
|
-
app -> res: release (automatic)
|
|
207
|
-
res -> ext: cleanup
|
|
208
|
-
|
|
209
|
-
note over res
|
|
210
|
-
Resource は例外が発生しても
|
|
211
|
-
必ず release を実行する
|
|
212
|
-
end note
|
|
213
|
-
|
|
214
|
-
@enduml
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
### 11.5 キャッシュの実装
|
|
218
|
-
|
|
219
|
-
`Ref<T>` を使用したスレッドセーフなキャッシュの実装:
|
|
220
|
-
|
|
221
|
-
```csharp
|
|
222
|
-
/// <summary>
|
|
223
|
-
/// キャッシュ付きデータアクセスを作成
|
|
224
|
-
/// </summary>
|
|
225
|
-
public static IDataAccess CachedDataAccess(IDataAccess dataAccess)
|
|
226
|
-
{
|
|
227
|
-
var attractionCache = ConcurrentProcessing.Ref<Map<string, Seq<Attraction>>>.Of(
|
|
228
|
-
Map<string, Seq<Attraction>>());
|
|
229
|
-
var artistCache = ConcurrentProcessing.Ref<Map<string, Seq<MusicArtist>>>.Of(
|
|
230
|
-
Map<string, Seq<MusicArtist>>());
|
|
231
|
-
var movieCache = ConcurrentProcessing.Ref<Map<string, Seq<Movie>>>.Of(
|
|
232
|
-
Map<string, Seq<Movie>>());
|
|
233
|
-
|
|
234
|
-
return new CachedDataAccessImpl(dataAccess, attractionCache, artistCache, movieCache);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
private class CachedDataAccessImpl : IDataAccess
|
|
238
|
-
{
|
|
239
|
-
// キャッシュロジック: キーがあればキャッシュから返却、なければ取得して保存
|
|
240
|
-
public async Task<Seq<Attraction>> FindAttractions(string name, AttractionOrdering ordering, int limit)
|
|
241
|
-
{
|
|
242
|
-
var key = $"{name}-{ordering}-{limit}";
|
|
243
|
-
var cached = _attractionCache.Get().Find(key);
|
|
244
|
-
if (cached.IsSome)
|
|
245
|
-
{
|
|
246
|
-
return cached.IfNone(Seq<Attraction>());
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
var attractions = await _dataAccess.FindAttractions(name, ordering, limit);
|
|
250
|
-
_attractionCache.Update(cache => cache.Add(key, attractions));
|
|
251
|
-
return attractions;
|
|
252
|
-
}
|
|
253
|
-
// 他のメソッドも同様に実装...
|
|
254
|
-
}
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
```plantuml
|
|
258
|
-
@startuml
|
|
259
|
-
!theme plain
|
|
260
|
-
skinparam activity {
|
|
261
|
-
BackgroundColor White
|
|
262
|
-
BorderColor Black
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
start
|
|
266
|
-
|
|
267
|
-
:キャッシュキー生成;
|
|
268
|
-
|
|
269
|
-
:cache.Get() でキャッシュ確認;
|
|
270
|
-
|
|
271
|
-
if (キャッシュにデータあり?) then (yes)
|
|
272
|
-
:キャッシュからデータ返却;
|
|
273
|
-
else (no)
|
|
274
|
-
:外部 API を呼び出し;
|
|
275
|
-
:結果をキャッシュに保存;
|
|
276
|
-
:結果を返却;
|
|
277
|
-
endif
|
|
278
|
-
|
|
279
|
-
stop
|
|
280
|
-
|
|
281
|
-
note right
|
|
282
|
-
Ref を使用した
|
|
283
|
-
スレッドセーフなキャッシュ
|
|
284
|
-
end note
|
|
285
|
-
|
|
286
|
-
@enduml
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
### 11.6 アプリケーションの組み立て
|
|
290
|
-
|
|
291
|
-
すべてのコンポーネントを組み合わせてアプリケーションを構築します。
|
|
292
|
-
|
|
293
|
-
```csharp
|
|
294
|
-
/// <summary>
|
|
295
|
-
/// 旅行ガイドを生成
|
|
296
|
-
/// </summary>
|
|
297
|
-
public static async Task<Option<TravelGuide>> GetTravelGuide(IDataAccess data, string attractionName)
|
|
298
|
-
{
|
|
299
|
-
var attractions = await data.FindAttractions(attractionName, AttractionOrdering.ByLocationPopulation, 1);
|
|
300
|
-
if (attractions.IsEmpty)
|
|
301
|
-
{
|
|
302
|
-
return None;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
var attraction = attractions.First();
|
|
306
|
-
var artists = await data.FindArtistsFromLocation(attraction.Location.Id, 2);
|
|
307
|
-
var movies = await data.FindMoviesAboutLocation(attraction.Location.Id, 2);
|
|
308
|
-
|
|
309
|
-
var subjects = artists.Map(a => a.Name).Concat(movies.Map(m => m.Name));
|
|
310
|
-
|
|
311
|
-
return Some(new TravelGuide(attraction, subjects));
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
/// <summary>
|
|
315
|
-
/// 複数のアトラクションから旅行ガイドを生成
|
|
316
|
-
/// </summary>
|
|
317
|
-
public static async Task<Seq<TravelGuide>> GetTravelGuides(IDataAccess data, string attractionName, int limit)
|
|
318
|
-
{
|
|
319
|
-
var attractions = await data.FindAttractions(attractionName, AttractionOrdering.ByLocationPopulation, limit);
|
|
320
|
-
|
|
321
|
-
var guideTasks = attractions.Map(async attraction =>
|
|
322
|
-
{
|
|
323
|
-
var artists = await data.FindArtistsFromLocation(attraction.Location.Id, 2);
|
|
324
|
-
var movies = await data.FindMoviesAboutLocation(attraction.Location.Id, 2);
|
|
325
|
-
var subjects = artists.Map(a => a.Name).Concat(movies.Map(m => m.Name));
|
|
326
|
-
return new TravelGuide(attraction, subjects);
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
var guides = await Task.WhenAll(guideTasks);
|
|
330
|
-
return toSeq(guides);
|
|
331
|
-
}
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
```plantuml
|
|
335
|
-
@startuml
|
|
336
|
-
!theme plain
|
|
337
|
-
|
|
338
|
-
rectangle "GetTravelGuide 関数" {
|
|
339
|
-
card "1. アトラクション検索" as step1
|
|
340
|
-
card "2. アーティスト検索" as step2
|
|
341
|
-
card "3. 映画検索" as step3
|
|
342
|
-
card "4. TravelGuide 組み立て" as step4
|
|
343
|
-
|
|
344
|
-
step1 --> step2 : attraction
|
|
345
|
-
step1 --> step3 : location.Id
|
|
346
|
-
step2 --> step4 : artists
|
|
347
|
-
step3 --> step4 : movies
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
note bottom
|
|
351
|
-
async/await で
|
|
352
|
-
複数の Task を合成
|
|
353
|
-
end note
|
|
354
|
-
|
|
355
|
-
@enduml
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
---
|
|
359
|
-
|
|
360
|
-
## 第12章: テスト戦略
|
|
361
|
-
|
|
362
|
-
### 12.1 関数型プログラミングのテスト
|
|
363
|
-
|
|
364
|
-
関数型プログラミングでは、純粋関数のおかげでテストが非常に簡単になります。
|
|
365
|
-
|
|
366
|
-
```plantuml
|
|
367
|
-
@startuml
|
|
368
|
-
!theme plain
|
|
369
|
-
|
|
370
|
-
rectangle "テストの種類" {
|
|
371
|
-
rectangle "単体テスト" as unit {
|
|
372
|
-
card "純粋関数のテスト"
|
|
373
|
-
card "高速・独立"
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
rectangle "プロパティベーステスト" as property {
|
|
377
|
-
card "ランダム入力"
|
|
378
|
-
card "不変条件の検証"
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
rectangle "統合テスト" as integration {
|
|
382
|
-
card "コンポーネント連携"
|
|
383
|
-
card "スタブ/モック使用"
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
unit --> property : 補完
|
|
388
|
-
property --> integration : 補完
|
|
389
|
-
|
|
390
|
-
@enduml
|
|
391
|
-
```
|
|
392
|
-
|
|
393
|
-
### 12.2 テスト用スタブの実装
|
|
394
|
-
|
|
395
|
-
**ソースファイル**: `app/csharp/tests/Ch11/TravelGuideTests.cs`
|
|
396
|
-
|
|
397
|
-
テスト可能性を高めるため、テスト用のスタブ実装を用意します。
|
|
398
|
-
|
|
399
|
-
```csharp
|
|
400
|
-
/// <summary>
|
|
401
|
-
/// テスト用のデータアクセス実装
|
|
402
|
-
/// </summary>
|
|
403
|
-
public static IDataAccess TestDataAccess(
|
|
404
|
-
Seq<Attraction> testAttractions,
|
|
405
|
-
Seq<MusicArtist> testArtists,
|
|
406
|
-
Seq<Movie> testMovies)
|
|
407
|
-
{
|
|
408
|
-
return new TestDataAccessImpl(testAttractions, testArtists, testMovies);
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
private class TestDataAccessImpl : IDataAccess
|
|
412
|
-
{
|
|
413
|
-
private readonly Seq<Attraction> _attractions;
|
|
414
|
-
private readonly Seq<MusicArtist> _artists;
|
|
415
|
-
private readonly Seq<Movie> _movies;
|
|
416
|
-
|
|
417
|
-
public TestDataAccessImpl(Seq<Attraction> attractions, Seq<MusicArtist> artists, Seq<Movie> movies)
|
|
418
|
-
{
|
|
419
|
-
_attractions = attractions;
|
|
420
|
-
_artists = artists;
|
|
421
|
-
_movies = movies;
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
public Task<Seq<Attraction>> FindAttractions(string name, AttractionOrdering ordering, int limit)
|
|
425
|
-
{
|
|
426
|
-
var filtered = _attractions
|
|
427
|
-
.Filter(a => a.Name.Contains(name))
|
|
428
|
-
.Take(limit);
|
|
429
|
-
return Task.FromResult(filtered);
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
public Task<Seq<MusicArtist>> FindArtistsFromLocation(LocationId locationId, int limit)
|
|
433
|
-
{
|
|
434
|
-
return Task.FromResult(_artists.Take(limit));
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
public Task<Seq<Movie>> FindMoviesAboutLocation(LocationId locationId, int limit)
|
|
438
|
-
{
|
|
439
|
-
return Task.FromResult(_movies.Take(limit));
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
```
|
|
443
|
-
|
|
444
|
-
### 12.3 単体テストの実装
|
|
445
|
-
|
|
446
|
-
```csharp
|
|
447
|
-
public class GetTravelGuideTests
|
|
448
|
-
{
|
|
449
|
-
[Fact]
|
|
450
|
-
public async Task GetTravelGuide_ReturnsGuideForValidAttraction()
|
|
451
|
-
{
|
|
452
|
-
var testLocation = new Location(
|
|
453
|
-
LocationId.Create("Q123"),
|
|
454
|
-
"Test City",
|
|
455
|
-
100000
|
|
456
|
-
);
|
|
457
|
-
var testAttractions = Seq(
|
|
458
|
-
new Attraction("Test Tower", Some("A tower"), testLocation)
|
|
459
|
-
);
|
|
460
|
-
var testArtists = Seq(
|
|
461
|
-
new MusicArtist("Artist1", None),
|
|
462
|
-
new MusicArtist("Artist2", Some("Rock"))
|
|
463
|
-
);
|
|
464
|
-
var testMovies = Seq(
|
|
465
|
-
new Movie("Movie1", None)
|
|
466
|
-
);
|
|
467
|
-
|
|
468
|
-
var dataAccess = TestDataAccess(testAttractions, testArtists, testMovies);
|
|
469
|
-
var result = await GetTravelGuide(dataAccess, "Test");
|
|
470
|
-
|
|
471
|
-
Assert.True(result.IsSome);
|
|
472
|
-
var guide = result.IfNone(() => throw new Exception("Expected Some"));
|
|
473
|
-
Assert.Equal("Test Tower", guide.Attraction.Name);
|
|
474
|
-
Assert.Equal(3, guide.Subjects.Count); // 2 artists + 1 movie
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
[Fact]
|
|
478
|
-
public async Task GetTravelGuide_ReturnsNoneWhenNotFound()
|
|
479
|
-
{
|
|
480
|
-
var dataAccess = TestDataAccess(Seq<Attraction>(), Seq<MusicArtist>(), Seq<Movie>());
|
|
481
|
-
var result = await GetTravelGuide(dataAccess, "NonExistent");
|
|
482
|
-
|
|
483
|
-
Assert.True(result.IsNone);
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
```
|
|
487
|
-
|
|
488
|
-
### 12.4 キャッシュのテスト
|
|
489
|
-
|
|
490
|
-
```csharp
|
|
491
|
-
public class CachedDataAccessTests
|
|
492
|
-
{
|
|
493
|
-
[Fact]
|
|
494
|
-
public async Task CachedDataAccess_CachesData()
|
|
495
|
-
{
|
|
496
|
-
var callCount = 0;
|
|
497
|
-
var testLocation = new Location(
|
|
498
|
-
LocationId.Create("Q123"),
|
|
499
|
-
"Test City",
|
|
500
|
-
100000
|
|
501
|
-
);
|
|
502
|
-
var testAttractions = Seq(
|
|
503
|
-
new Attraction("Test Tower", None, testLocation)
|
|
504
|
-
);
|
|
505
|
-
|
|
506
|
-
var countingDataAccess = new CountingDataAccess(
|
|
507
|
-
testAttractions,
|
|
508
|
-
Seq<MusicArtist>(),
|
|
509
|
-
Seq<Movie>(),
|
|
510
|
-
() => callCount++
|
|
511
|
-
);
|
|
512
|
-
|
|
513
|
-
var cached = CachedDataAccess(countingDataAccess);
|
|
514
|
-
|
|
515
|
-
// First call
|
|
516
|
-
_ = await cached.FindAttractions("Test", AttractionOrdering.ByName, 10);
|
|
517
|
-
Assert.Equal(1, callCount);
|
|
518
|
-
|
|
519
|
-
// Second call (should be cached)
|
|
520
|
-
_ = await cached.FindAttractions("Test", AttractionOrdering.ByName, 10);
|
|
521
|
-
Assert.Equal(1, callCount); // Call count should not increase
|
|
522
|
-
|
|
523
|
-
// Different parameters
|
|
524
|
-
_ = await cached.FindAttractions("Other", AttractionOrdering.ByName, 10);
|
|
525
|
-
Assert.Equal(2, callCount);
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
```
|
|
529
|
-
|
|
530
|
-
### 12.5 Resource のテスト
|
|
531
|
-
|
|
532
|
-
```csharp
|
|
533
|
-
public class ResourceTests
|
|
534
|
-
{
|
|
535
|
-
[Fact]
|
|
536
|
-
public async Task Resource_Make_CreatesResource()
|
|
537
|
-
{
|
|
538
|
-
var acquired = false;
|
|
539
|
-
var released = false;
|
|
540
|
-
|
|
541
|
-
var resource = Resource.Make(
|
|
542
|
-
async () =>
|
|
543
|
-
{
|
|
544
|
-
acquired = true;
|
|
545
|
-
return "resource";
|
|
546
|
-
},
|
|
547
|
-
async r => { released = true; }
|
|
548
|
-
);
|
|
549
|
-
|
|
550
|
-
var result = await resource.UseAsync(async r => r + "-used");
|
|
551
|
-
|
|
552
|
-
Assert.Equal("resource-used", result);
|
|
553
|
-
Assert.True(acquired);
|
|
554
|
-
Assert.True(released);
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
[Fact]
|
|
558
|
-
public async Task Resource_ReleasesEvenOnException()
|
|
559
|
-
{
|
|
560
|
-
var released = false;
|
|
561
|
-
|
|
562
|
-
var resource = Resource.Make(
|
|
563
|
-
async () => "resource",
|
|
564
|
-
async r => { released = true; }
|
|
565
|
-
);
|
|
566
|
-
|
|
567
|
-
try
|
|
568
|
-
{
|
|
569
|
-
await resource.UseAsync<string>(async r => throw new Exception("Test error"));
|
|
570
|
-
}
|
|
571
|
-
catch
|
|
572
|
-
{
|
|
573
|
-
// Expected
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
Assert.True(released);
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
```
|
|
580
|
-
|
|
581
|
-
```plantuml
|
|
582
|
-
@startuml
|
|
583
|
-
!theme plain
|
|
584
|
-
|
|
585
|
-
rectangle "テスト構成" {
|
|
586
|
-
rectangle "本番" as prod {
|
|
587
|
-
interface "IDataAccess" as da1
|
|
588
|
-
class "RealDataAccess" as rda
|
|
589
|
-
da1 <|.. rda
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
rectangle "テスト" as test {
|
|
593
|
-
interface "IDataAccess" as da2
|
|
594
|
-
class "TestDataAccess" as tda
|
|
595
|
-
da2 <|.. tda
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
note bottom of prod
|
|
600
|
-
実際の外部 API に接続
|
|
601
|
-
end note
|
|
602
|
-
|
|
603
|
-
note bottom of test
|
|
604
|
-
テストデータを返す
|
|
605
|
-
Task.FromResult で即座に結果を返す
|
|
606
|
-
end note
|
|
607
|
-
|
|
608
|
-
@enduml
|
|
609
|
-
```
|
|
610
|
-
|
|
611
|
-
### 12.6 テストピラミッド
|
|
612
|
-
|
|
613
|
-
```plantuml
|
|
614
|
-
@startuml
|
|
615
|
-
!theme plain
|
|
616
|
-
|
|
617
|
-
rectangle "テストピラミッド" {
|
|
618
|
-
rectangle "E2E テスト\n(少数)" as e2e
|
|
619
|
-
rectangle "統合テスト\n(中程度)" as integration
|
|
620
|
-
rectangle "単体テスト + プロパティテスト\n(多数)" as unit
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
e2e -[hidden]down- integration
|
|
624
|
-
integration -[hidden]down- unit
|
|
625
|
-
|
|
626
|
-
note right of unit
|
|
627
|
-
FP では純粋関数が多いため
|
|
628
|
-
単体テストが非常に効果的
|
|
629
|
-
end note
|
|
630
|
-
|
|
631
|
-
@enduml
|
|
632
|
-
```
|
|
633
|
-
|
|
634
|
-
---
|
|
635
|
-
|
|
636
|
-
## まとめ
|
|
637
|
-
|
|
638
|
-
### Part VI で学んだこと
|
|
639
|
-
|
|
640
|
-
```plantuml
|
|
641
|
-
@startuml
|
|
642
|
-
!theme plain
|
|
643
|
-
|
|
644
|
-
rectangle "Part VI: 実践的なアプリケーション" {
|
|
645
|
-
rectangle "第11章" as ch11 {
|
|
646
|
-
card "ドメインモデル設計"
|
|
647
|
-
card "IDataAccess 抽象化"
|
|
648
|
-
card "Resource 管理"
|
|
649
|
-
card "キャッシュ実装"
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
rectangle "第12章" as ch12 {
|
|
653
|
-
card "テスト用スタブ"
|
|
654
|
-
card "単体テスト"
|
|
655
|
-
card "統合テスト"
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
ch11 --> ch12
|
|
660
|
-
|
|
661
|
-
@enduml
|
|
662
|
-
```
|
|
663
|
-
|
|
664
|
-
### キーポイント
|
|
665
|
-
|
|
666
|
-
1. **抽象化の重要性**: IDataAccess インターフェースで外部依存を抽象化
|
|
667
|
-
2. **Resource でリソース管理**: 安全なリソースの取得と解放
|
|
668
|
-
3. **Ref でキャッシュ**: スレッドセーフな状態管理
|
|
669
|
-
4. **Option でエラー処理**: 明示的なエラーハンドリング
|
|
670
|
-
5. **スタブ**: 外部依存を差し替えてテスト
|
|
671
|
-
6. **record 型**: イミュータブルなドメインモデル
|
|
672
|
-
|
|
673
|
-
### 学習の総括
|
|
674
|
-
|
|
675
|
-
```plantuml
|
|
676
|
-
@startuml
|
|
677
|
-
!theme plain
|
|
678
|
-
left to right direction
|
|
679
|
-
|
|
680
|
-
rectangle "C# FP の学習パス" {
|
|
681
|
-
card "Part I\n基礎" as p1
|
|
682
|
-
card "Part II\n関数型スタイル" as p2
|
|
683
|
-
card "Part III\nエラーハンドリング" as p3
|
|
684
|
-
card "Part IV\n非同期/ストリーム" as p4
|
|
685
|
-
card "Part V\n並行処理" as p5
|
|
686
|
-
card "Part VI\n実践" as p6
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
p1 --> p2
|
|
690
|
-
p2 --> p3
|
|
691
|
-
p3 --> p4
|
|
692
|
-
p4 --> p5
|
|
693
|
-
p5 --> p6
|
|
694
|
-
|
|
695
|
-
@enduml
|
|
696
|
-
```
|
|
697
|
-
|
|
698
|
-
---
|
|
699
|
-
|
|
700
|
-
## シリーズ全体の総括
|
|
701
|
-
|
|
702
|
-
本シリーズでは、「Grokking Functional Programming」の内容に沿って、関数型プログラミングの基礎から実践的なアプリケーション構築までを C# と LanguageExt ライブラリで学びました。
|
|
703
|
-
|
|
704
|
-
### 学んだ主な概念
|
|
705
|
-
|
|
706
|
-
| Part | 章 | 主な概念 |
|
|
707
|
-
|------|-----|----------|
|
|
708
|
-
| I | 1-2 | 純粋関数、参照透過性 |
|
|
709
|
-
| II | 3-5 | イミュータブルデータ、高階関数、Bind |
|
|
710
|
-
| III | 6-7 | Option、Either/Fin |
|
|
711
|
-
| IV | 8-9 | Task(非同期)、IAsyncEnumerable(ストリーム) |
|
|
712
|
-
| V | 10 | 並行処理、Ref、Agent |
|
|
713
|
-
| VI | 11 | 実践アプリケーション、テスト |
|
|
714
|
-
|
|
715
|
-
### 関数型プログラミングの利点
|
|
716
|
-
|
|
717
|
-
1. **予測可能性**: 純粋関数は同じ入力に対して常に同じ出力
|
|
718
|
-
2. **テスト容易性**: 副作用がないためテストが簡単
|
|
719
|
-
3. **合成可能性**: 小さな関数を組み合わせて複雑な処理を構築
|
|
720
|
-
4. **並行安全性**: イミュータブルデータは競合状態を防ぐ
|
|
721
|
-
5. **型安全性**: Option、Either で null や例外を型で表現
|
|
722
|
-
|
|
723
|
-
### C# + LanguageExt の特徴
|
|
724
|
-
|
|
725
|
-
C# は .NET プラットフォーム上で動作するマルチパラダイム言語で、LanguageExt ライブラリと組み合わせることで以下の特徴を持ちます:
|
|
726
|
-
|
|
727
|
-
- **Option<T>**: null 安全なオプション型
|
|
728
|
-
- **Either<L, R>**: 成功/失敗を型で表現
|
|
729
|
-
- **Seq<T>**: イミュータブルなシーケンス
|
|
730
|
-
- **Map<K, V>**: イミュータブルな辞書
|
|
731
|
-
- **パターンマッチング**: switch 式で関数型スタイルの分岐
|
|
732
|
-
- **record 型**: イミュータブルなデータ型
|
|
733
|
-
|
|
734
|
-
### 次のステップ
|
|
735
|
-
|
|
736
|
-
- C# の高度な機能(Source Generators、拡張メソッド)を学ぶ
|
|
737
|
-
- ASP.NET Core で FP スタイルの Web API を構築
|
|
738
|
-
- より高度な LanguageExt 機能(Eff, Aff)を探索
|
|
739
|
-
- 実際のプロジェクトで FP を適用する
|
|
1
|
+
# Part VI: 実践的なアプリケーション構築とテスト
|
|
2
|
+
|
|
3
|
+
本章では、これまで学んだ関数型プログラミングの概念を統合し、実践的なアプリケーションを構築します。また、関数型プログラミングにおけるテスト戦略についても学びます。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 第11章: 実践的なアプリケーション構築
|
|
8
|
+
|
|
9
|
+
### 11.1 TravelGuide アプリケーション
|
|
10
|
+
|
|
11
|
+
旅行ガイドアプリケーションを例に、実践的な FP アプリケーションの構築方法を学びます。
|
|
12
|
+
|
|
13
|
+
```plantuml
|
|
14
|
+
@startuml
|
|
15
|
+
!theme plain
|
|
16
|
+
|
|
17
|
+
package "TravelGuide Application" {
|
|
18
|
+
rectangle "Model" {
|
|
19
|
+
class Location {
|
|
20
|
+
Id: LocationId
|
|
21
|
+
Name: string
|
|
22
|
+
Population: int
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
class Attraction {
|
|
26
|
+
Name: string
|
|
27
|
+
Description: Option<string>
|
|
28
|
+
Location: Location
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
class TravelGuide {
|
|
32
|
+
Attraction: Attraction
|
|
33
|
+
Subjects: Seq<string>
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
rectangle "Data Access" {
|
|
38
|
+
interface IDataAccess {
|
|
39
|
+
+FindAttractions(): Task<Seq<Attraction>>
|
|
40
|
+
+FindArtistsFromLocation(): Task<Seq<MusicArtist>>
|
|
41
|
+
+FindMoviesAboutLocation(): Task<Seq<Movie>>
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
class TestDataAccess
|
|
45
|
+
class CachedDataAccess
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
IDataAccess <|.. TestDataAccess
|
|
49
|
+
IDataAccess <|.. CachedDataAccess
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@enduml
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 11.2 ドメインモデルの定義
|
|
56
|
+
|
|
57
|
+
**ソースファイル**: `app/csharp/src/Ch11/TravelGuide.cs`
|
|
58
|
+
|
|
59
|
+
```csharp
|
|
60
|
+
/// <summary>
|
|
61
|
+
/// ロケーション ID(値オブジェクト)
|
|
62
|
+
/// </summary>
|
|
63
|
+
public readonly record struct LocationId(string Value)
|
|
64
|
+
{
|
|
65
|
+
public static LocationId Create(string id) => new(id);
|
|
66
|
+
public override string ToString() => Value;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/// <summary>
|
|
70
|
+
/// ロケーション
|
|
71
|
+
/// </summary>
|
|
72
|
+
public record Location(LocationId Id, string Name, int Population);
|
|
73
|
+
|
|
74
|
+
/// <summary>
|
|
75
|
+
/// アトラクション(観光地)
|
|
76
|
+
/// </summary>
|
|
77
|
+
public record Attraction(string Name, Option<string> Description, Location Location);
|
|
78
|
+
|
|
79
|
+
/// <summary>
|
|
80
|
+
/// ミュージックアーティスト
|
|
81
|
+
/// </summary>
|
|
82
|
+
public record MusicArtist(string Name, Option<string> Genre);
|
|
83
|
+
|
|
84
|
+
/// <summary>
|
|
85
|
+
/// 映画
|
|
86
|
+
/// </summary>
|
|
87
|
+
public record Movie(string Name, Option<int> BoxOffice);
|
|
88
|
+
|
|
89
|
+
/// <summary>
|
|
90
|
+
/// 旅行ガイド
|
|
91
|
+
/// </summary>
|
|
92
|
+
public record TravelGuide(Attraction Attraction, Seq<string> Subjects);
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### 11.3 データアクセス層の抽象化
|
|
96
|
+
|
|
97
|
+
外部データソースへのアクセスをインターフェースで抽象化します。
|
|
98
|
+
|
|
99
|
+
```csharp
|
|
100
|
+
/// <summary>
|
|
101
|
+
/// データアクセスインターフェース
|
|
102
|
+
/// </summary>
|
|
103
|
+
public interface IDataAccess
|
|
104
|
+
{
|
|
105
|
+
Task<Seq<Attraction>> FindAttractions(string name, AttractionOrdering ordering, int limit);
|
|
106
|
+
Task<Seq<MusicArtist>> FindArtistsFromLocation(LocationId locationId, int limit);
|
|
107
|
+
Task<Seq<Movie>> FindMoviesAboutLocation(LocationId locationId, int limit);
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
```plantuml
|
|
112
|
+
@startuml
|
|
113
|
+
!theme plain
|
|
114
|
+
|
|
115
|
+
interface IDataAccess {
|
|
116
|
+
+FindAttractions(): Task<Seq<Attraction>>
|
|
117
|
+
+FindArtistsFromLocation(): Task<Seq<MusicArtist>>
|
|
118
|
+
+FindMoviesAboutLocation(): Task<Seq<Movie>>
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
note right of IDataAccess
|
|
122
|
+
純粋な関数型インターフェース
|
|
123
|
+
すべての操作は Task でラップ
|
|
124
|
+
end note
|
|
125
|
+
|
|
126
|
+
class TestDataAccess {
|
|
127
|
+
-testAttractions: Seq<Attraction>
|
|
128
|
+
+FindAttractions(): Task<Seq<Attraction>>
|
|
129
|
+
+FindArtistsFromLocation(): Task<Seq<MusicArtist>>
|
|
130
|
+
+FindMoviesAboutLocation(): Task<Seq<Movie>>
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
class CachedDataAccess {
|
|
134
|
+
-cache: Ref<Map<string, T>>
|
|
135
|
+
+FindAttractions(): Task<Seq<Attraction>>
|
|
136
|
+
+FindArtistsFromLocation(): Task<Seq<MusicArtist>>
|
|
137
|
+
+FindMoviesAboutLocation(): Task<Seq<Movie>>
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
IDataAccess <|.. TestDataAccess
|
|
141
|
+
IDataAccess <|.. CachedDataAccess
|
|
142
|
+
|
|
143
|
+
@enduml
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### 11.4 Resource によるリソース管理
|
|
147
|
+
|
|
148
|
+
C# では `IDisposable` パターンと `using` ステートメントでリソースを管理できますが、より関数型的な `Resource<T>` 型を実装することもできます。
|
|
149
|
+
|
|
150
|
+
```csharp
|
|
151
|
+
/// <summary>
|
|
152
|
+
/// リソースを安全に管理するための型
|
|
153
|
+
/// </summary>
|
|
154
|
+
public class Resource<T>
|
|
155
|
+
{
|
|
156
|
+
private readonly Func<Task<T>> _acquire;
|
|
157
|
+
private readonly Func<T, Task> _release;
|
|
158
|
+
|
|
159
|
+
private Resource(Func<Task<T>> acquire, Func<T, Task> release)
|
|
160
|
+
{
|
|
161
|
+
_acquire = acquire;
|
|
162
|
+
_release = release;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/// <summary>
|
|
166
|
+
/// リソースを作成
|
|
167
|
+
/// </summary>
|
|
168
|
+
public static Resource<T> Make(Func<Task<T>> acquire, Func<T, Task> release)
|
|
169
|
+
=> new(acquire, release);
|
|
170
|
+
|
|
171
|
+
/// <summary>
|
|
172
|
+
/// リソースを使用して操作を実行
|
|
173
|
+
/// </summary>
|
|
174
|
+
public async Task<TResult> UseAsync<TResult>(Func<T, Task<TResult>> f)
|
|
175
|
+
{
|
|
176
|
+
var resource = await _acquire();
|
|
177
|
+
try
|
|
178
|
+
{
|
|
179
|
+
return await f(resource);
|
|
180
|
+
}
|
|
181
|
+
finally
|
|
182
|
+
{
|
|
183
|
+
await _release(resource);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
```plantuml
|
|
190
|
+
@startuml
|
|
191
|
+
!theme plain
|
|
192
|
+
|
|
193
|
+
participant "Application" as app
|
|
194
|
+
participant "Resource" as res
|
|
195
|
+
participant "External Resource" as ext
|
|
196
|
+
|
|
197
|
+
app -> res: Resource.Make(acquire, release)
|
|
198
|
+
res -> ext: acquire
|
|
199
|
+
ext --> res: resource
|
|
200
|
+
|
|
201
|
+
app -> res: UseAsync(operation)
|
|
202
|
+
res -> ext: execute operation
|
|
203
|
+
ext --> res: result
|
|
204
|
+
res --> app: result
|
|
205
|
+
|
|
206
|
+
app -> res: release (automatic)
|
|
207
|
+
res -> ext: cleanup
|
|
208
|
+
|
|
209
|
+
note over res
|
|
210
|
+
Resource は例外が発生しても
|
|
211
|
+
必ず release を実行する
|
|
212
|
+
end note
|
|
213
|
+
|
|
214
|
+
@enduml
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### 11.5 キャッシュの実装
|
|
218
|
+
|
|
219
|
+
`Ref<T>` を使用したスレッドセーフなキャッシュの実装:
|
|
220
|
+
|
|
221
|
+
```csharp
|
|
222
|
+
/// <summary>
|
|
223
|
+
/// キャッシュ付きデータアクセスを作成
|
|
224
|
+
/// </summary>
|
|
225
|
+
public static IDataAccess CachedDataAccess(IDataAccess dataAccess)
|
|
226
|
+
{
|
|
227
|
+
var attractionCache = ConcurrentProcessing.Ref<Map<string, Seq<Attraction>>>.Of(
|
|
228
|
+
Map<string, Seq<Attraction>>());
|
|
229
|
+
var artistCache = ConcurrentProcessing.Ref<Map<string, Seq<MusicArtist>>>.Of(
|
|
230
|
+
Map<string, Seq<MusicArtist>>());
|
|
231
|
+
var movieCache = ConcurrentProcessing.Ref<Map<string, Seq<Movie>>>.Of(
|
|
232
|
+
Map<string, Seq<Movie>>());
|
|
233
|
+
|
|
234
|
+
return new CachedDataAccessImpl(dataAccess, attractionCache, artistCache, movieCache);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
private class CachedDataAccessImpl : IDataAccess
|
|
238
|
+
{
|
|
239
|
+
// キャッシュロジック: キーがあればキャッシュから返却、なければ取得して保存
|
|
240
|
+
public async Task<Seq<Attraction>> FindAttractions(string name, AttractionOrdering ordering, int limit)
|
|
241
|
+
{
|
|
242
|
+
var key = $"{name}-{ordering}-{limit}";
|
|
243
|
+
var cached = _attractionCache.Get().Find(key);
|
|
244
|
+
if (cached.IsSome)
|
|
245
|
+
{
|
|
246
|
+
return cached.IfNone(Seq<Attraction>());
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
var attractions = await _dataAccess.FindAttractions(name, ordering, limit);
|
|
250
|
+
_attractionCache.Update(cache => cache.Add(key, attractions));
|
|
251
|
+
return attractions;
|
|
252
|
+
}
|
|
253
|
+
// 他のメソッドも同様に実装...
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
```plantuml
|
|
258
|
+
@startuml
|
|
259
|
+
!theme plain
|
|
260
|
+
skinparam activity {
|
|
261
|
+
BackgroundColor White
|
|
262
|
+
BorderColor Black
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
start
|
|
266
|
+
|
|
267
|
+
:キャッシュキー生成;
|
|
268
|
+
|
|
269
|
+
:cache.Get() でキャッシュ確認;
|
|
270
|
+
|
|
271
|
+
if (キャッシュにデータあり?) then (yes)
|
|
272
|
+
:キャッシュからデータ返却;
|
|
273
|
+
else (no)
|
|
274
|
+
:外部 API を呼び出し;
|
|
275
|
+
:結果をキャッシュに保存;
|
|
276
|
+
:結果を返却;
|
|
277
|
+
endif
|
|
278
|
+
|
|
279
|
+
stop
|
|
280
|
+
|
|
281
|
+
note right
|
|
282
|
+
Ref を使用した
|
|
283
|
+
スレッドセーフなキャッシュ
|
|
284
|
+
end note
|
|
285
|
+
|
|
286
|
+
@enduml
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### 11.6 アプリケーションの組み立て
|
|
290
|
+
|
|
291
|
+
すべてのコンポーネントを組み合わせてアプリケーションを構築します。
|
|
292
|
+
|
|
293
|
+
```csharp
|
|
294
|
+
/// <summary>
|
|
295
|
+
/// 旅行ガイドを生成
|
|
296
|
+
/// </summary>
|
|
297
|
+
public static async Task<Option<TravelGuide>> GetTravelGuide(IDataAccess data, string attractionName)
|
|
298
|
+
{
|
|
299
|
+
var attractions = await data.FindAttractions(attractionName, AttractionOrdering.ByLocationPopulation, 1);
|
|
300
|
+
if (attractions.IsEmpty)
|
|
301
|
+
{
|
|
302
|
+
return None;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
var attraction = attractions.First();
|
|
306
|
+
var artists = await data.FindArtistsFromLocation(attraction.Location.Id, 2);
|
|
307
|
+
var movies = await data.FindMoviesAboutLocation(attraction.Location.Id, 2);
|
|
308
|
+
|
|
309
|
+
var subjects = artists.Map(a => a.Name).Concat(movies.Map(m => m.Name));
|
|
310
|
+
|
|
311
|
+
return Some(new TravelGuide(attraction, subjects));
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/// <summary>
|
|
315
|
+
/// 複数のアトラクションから旅行ガイドを生成
|
|
316
|
+
/// </summary>
|
|
317
|
+
public static async Task<Seq<TravelGuide>> GetTravelGuides(IDataAccess data, string attractionName, int limit)
|
|
318
|
+
{
|
|
319
|
+
var attractions = await data.FindAttractions(attractionName, AttractionOrdering.ByLocationPopulation, limit);
|
|
320
|
+
|
|
321
|
+
var guideTasks = attractions.Map(async attraction =>
|
|
322
|
+
{
|
|
323
|
+
var artists = await data.FindArtistsFromLocation(attraction.Location.Id, 2);
|
|
324
|
+
var movies = await data.FindMoviesAboutLocation(attraction.Location.Id, 2);
|
|
325
|
+
var subjects = artists.Map(a => a.Name).Concat(movies.Map(m => m.Name));
|
|
326
|
+
return new TravelGuide(attraction, subjects);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
var guides = await Task.WhenAll(guideTasks);
|
|
330
|
+
return toSeq(guides);
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
```plantuml
|
|
335
|
+
@startuml
|
|
336
|
+
!theme plain
|
|
337
|
+
|
|
338
|
+
rectangle "GetTravelGuide 関数" {
|
|
339
|
+
card "1. アトラクション検索" as step1
|
|
340
|
+
card "2. アーティスト検索" as step2
|
|
341
|
+
card "3. 映画検索" as step3
|
|
342
|
+
card "4. TravelGuide 組み立て" as step4
|
|
343
|
+
|
|
344
|
+
step1 --> step2 : attraction
|
|
345
|
+
step1 --> step3 : location.Id
|
|
346
|
+
step2 --> step4 : artists
|
|
347
|
+
step3 --> step4 : movies
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
note bottom
|
|
351
|
+
async/await で
|
|
352
|
+
複数の Task を合成
|
|
353
|
+
end note
|
|
354
|
+
|
|
355
|
+
@enduml
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
## 第12章: テスト戦略
|
|
361
|
+
|
|
362
|
+
### 12.1 関数型プログラミングのテスト
|
|
363
|
+
|
|
364
|
+
関数型プログラミングでは、純粋関数のおかげでテストが非常に簡単になります。
|
|
365
|
+
|
|
366
|
+
```plantuml
|
|
367
|
+
@startuml
|
|
368
|
+
!theme plain
|
|
369
|
+
|
|
370
|
+
rectangle "テストの種類" {
|
|
371
|
+
rectangle "単体テスト" as unit {
|
|
372
|
+
card "純粋関数のテスト"
|
|
373
|
+
card "高速・独立"
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
rectangle "プロパティベーステスト" as property {
|
|
377
|
+
card "ランダム入力"
|
|
378
|
+
card "不変条件の検証"
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
rectangle "統合テスト" as integration {
|
|
382
|
+
card "コンポーネント連携"
|
|
383
|
+
card "スタブ/モック使用"
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
unit --> property : 補完
|
|
388
|
+
property --> integration : 補完
|
|
389
|
+
|
|
390
|
+
@enduml
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
### 12.2 テスト用スタブの実装
|
|
394
|
+
|
|
395
|
+
**ソースファイル**: `app/csharp/tests/Ch11/TravelGuideTests.cs`
|
|
396
|
+
|
|
397
|
+
テスト可能性を高めるため、テスト用のスタブ実装を用意します。
|
|
398
|
+
|
|
399
|
+
```csharp
|
|
400
|
+
/// <summary>
|
|
401
|
+
/// テスト用のデータアクセス実装
|
|
402
|
+
/// </summary>
|
|
403
|
+
public static IDataAccess TestDataAccess(
|
|
404
|
+
Seq<Attraction> testAttractions,
|
|
405
|
+
Seq<MusicArtist> testArtists,
|
|
406
|
+
Seq<Movie> testMovies)
|
|
407
|
+
{
|
|
408
|
+
return new TestDataAccessImpl(testAttractions, testArtists, testMovies);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
private class TestDataAccessImpl : IDataAccess
|
|
412
|
+
{
|
|
413
|
+
private readonly Seq<Attraction> _attractions;
|
|
414
|
+
private readonly Seq<MusicArtist> _artists;
|
|
415
|
+
private readonly Seq<Movie> _movies;
|
|
416
|
+
|
|
417
|
+
public TestDataAccessImpl(Seq<Attraction> attractions, Seq<MusicArtist> artists, Seq<Movie> movies)
|
|
418
|
+
{
|
|
419
|
+
_attractions = attractions;
|
|
420
|
+
_artists = artists;
|
|
421
|
+
_movies = movies;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
public Task<Seq<Attraction>> FindAttractions(string name, AttractionOrdering ordering, int limit)
|
|
425
|
+
{
|
|
426
|
+
var filtered = _attractions
|
|
427
|
+
.Filter(a => a.Name.Contains(name))
|
|
428
|
+
.Take(limit);
|
|
429
|
+
return Task.FromResult(filtered);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
public Task<Seq<MusicArtist>> FindArtistsFromLocation(LocationId locationId, int limit)
|
|
433
|
+
{
|
|
434
|
+
return Task.FromResult(_artists.Take(limit));
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
public Task<Seq<Movie>> FindMoviesAboutLocation(LocationId locationId, int limit)
|
|
438
|
+
{
|
|
439
|
+
return Task.FromResult(_movies.Take(limit));
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
### 12.3 単体テストの実装
|
|
445
|
+
|
|
446
|
+
```csharp
|
|
447
|
+
public class GetTravelGuideTests
|
|
448
|
+
{
|
|
449
|
+
[Fact]
|
|
450
|
+
public async Task GetTravelGuide_ReturnsGuideForValidAttraction()
|
|
451
|
+
{
|
|
452
|
+
var testLocation = new Location(
|
|
453
|
+
LocationId.Create("Q123"),
|
|
454
|
+
"Test City",
|
|
455
|
+
100000
|
|
456
|
+
);
|
|
457
|
+
var testAttractions = Seq(
|
|
458
|
+
new Attraction("Test Tower", Some("A tower"), testLocation)
|
|
459
|
+
);
|
|
460
|
+
var testArtists = Seq(
|
|
461
|
+
new MusicArtist("Artist1", None),
|
|
462
|
+
new MusicArtist("Artist2", Some("Rock"))
|
|
463
|
+
);
|
|
464
|
+
var testMovies = Seq(
|
|
465
|
+
new Movie("Movie1", None)
|
|
466
|
+
);
|
|
467
|
+
|
|
468
|
+
var dataAccess = TestDataAccess(testAttractions, testArtists, testMovies);
|
|
469
|
+
var result = await GetTravelGuide(dataAccess, "Test");
|
|
470
|
+
|
|
471
|
+
Assert.True(result.IsSome);
|
|
472
|
+
var guide = result.IfNone(() => throw new Exception("Expected Some"));
|
|
473
|
+
Assert.Equal("Test Tower", guide.Attraction.Name);
|
|
474
|
+
Assert.Equal(3, guide.Subjects.Count); // 2 artists + 1 movie
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
[Fact]
|
|
478
|
+
public async Task GetTravelGuide_ReturnsNoneWhenNotFound()
|
|
479
|
+
{
|
|
480
|
+
var dataAccess = TestDataAccess(Seq<Attraction>(), Seq<MusicArtist>(), Seq<Movie>());
|
|
481
|
+
var result = await GetTravelGuide(dataAccess, "NonExistent");
|
|
482
|
+
|
|
483
|
+
Assert.True(result.IsNone);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
### 12.4 キャッシュのテスト
|
|
489
|
+
|
|
490
|
+
```csharp
|
|
491
|
+
public class CachedDataAccessTests
|
|
492
|
+
{
|
|
493
|
+
[Fact]
|
|
494
|
+
public async Task CachedDataAccess_CachesData()
|
|
495
|
+
{
|
|
496
|
+
var callCount = 0;
|
|
497
|
+
var testLocation = new Location(
|
|
498
|
+
LocationId.Create("Q123"),
|
|
499
|
+
"Test City",
|
|
500
|
+
100000
|
|
501
|
+
);
|
|
502
|
+
var testAttractions = Seq(
|
|
503
|
+
new Attraction("Test Tower", None, testLocation)
|
|
504
|
+
);
|
|
505
|
+
|
|
506
|
+
var countingDataAccess = new CountingDataAccess(
|
|
507
|
+
testAttractions,
|
|
508
|
+
Seq<MusicArtist>(),
|
|
509
|
+
Seq<Movie>(),
|
|
510
|
+
() => callCount++
|
|
511
|
+
);
|
|
512
|
+
|
|
513
|
+
var cached = CachedDataAccess(countingDataAccess);
|
|
514
|
+
|
|
515
|
+
// First call
|
|
516
|
+
_ = await cached.FindAttractions("Test", AttractionOrdering.ByName, 10);
|
|
517
|
+
Assert.Equal(1, callCount);
|
|
518
|
+
|
|
519
|
+
// Second call (should be cached)
|
|
520
|
+
_ = await cached.FindAttractions("Test", AttractionOrdering.ByName, 10);
|
|
521
|
+
Assert.Equal(1, callCount); // Call count should not increase
|
|
522
|
+
|
|
523
|
+
// Different parameters
|
|
524
|
+
_ = await cached.FindAttractions("Other", AttractionOrdering.ByName, 10);
|
|
525
|
+
Assert.Equal(2, callCount);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
### 12.5 Resource のテスト
|
|
531
|
+
|
|
532
|
+
```csharp
|
|
533
|
+
public class ResourceTests
|
|
534
|
+
{
|
|
535
|
+
[Fact]
|
|
536
|
+
public async Task Resource_Make_CreatesResource()
|
|
537
|
+
{
|
|
538
|
+
var acquired = false;
|
|
539
|
+
var released = false;
|
|
540
|
+
|
|
541
|
+
var resource = Resource.Make(
|
|
542
|
+
async () =>
|
|
543
|
+
{
|
|
544
|
+
acquired = true;
|
|
545
|
+
return "resource";
|
|
546
|
+
},
|
|
547
|
+
async r => { released = true; }
|
|
548
|
+
);
|
|
549
|
+
|
|
550
|
+
var result = await resource.UseAsync(async r => r + "-used");
|
|
551
|
+
|
|
552
|
+
Assert.Equal("resource-used", result);
|
|
553
|
+
Assert.True(acquired);
|
|
554
|
+
Assert.True(released);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
[Fact]
|
|
558
|
+
public async Task Resource_ReleasesEvenOnException()
|
|
559
|
+
{
|
|
560
|
+
var released = false;
|
|
561
|
+
|
|
562
|
+
var resource = Resource.Make(
|
|
563
|
+
async () => "resource",
|
|
564
|
+
async r => { released = true; }
|
|
565
|
+
);
|
|
566
|
+
|
|
567
|
+
try
|
|
568
|
+
{
|
|
569
|
+
await resource.UseAsync<string>(async r => throw new Exception("Test error"));
|
|
570
|
+
}
|
|
571
|
+
catch
|
|
572
|
+
{
|
|
573
|
+
// Expected
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
Assert.True(released);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
```plantuml
|
|
582
|
+
@startuml
|
|
583
|
+
!theme plain
|
|
584
|
+
|
|
585
|
+
rectangle "テスト構成" {
|
|
586
|
+
rectangle "本番" as prod {
|
|
587
|
+
interface "IDataAccess" as da1
|
|
588
|
+
class "RealDataAccess" as rda
|
|
589
|
+
da1 <|.. rda
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
rectangle "テスト" as test {
|
|
593
|
+
interface "IDataAccess" as da2
|
|
594
|
+
class "TestDataAccess" as tda
|
|
595
|
+
da2 <|.. tda
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
note bottom of prod
|
|
600
|
+
実際の外部 API に接続
|
|
601
|
+
end note
|
|
602
|
+
|
|
603
|
+
note bottom of test
|
|
604
|
+
テストデータを返す
|
|
605
|
+
Task.FromResult で即座に結果を返す
|
|
606
|
+
end note
|
|
607
|
+
|
|
608
|
+
@enduml
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
### 12.6 テストピラミッド
|
|
612
|
+
|
|
613
|
+
```plantuml
|
|
614
|
+
@startuml
|
|
615
|
+
!theme plain
|
|
616
|
+
|
|
617
|
+
rectangle "テストピラミッド" {
|
|
618
|
+
rectangle "E2E テスト\n(少数)" as e2e
|
|
619
|
+
rectangle "統合テスト\n(中程度)" as integration
|
|
620
|
+
rectangle "単体テスト + プロパティテスト\n(多数)" as unit
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
e2e -[hidden]down- integration
|
|
624
|
+
integration -[hidden]down- unit
|
|
625
|
+
|
|
626
|
+
note right of unit
|
|
627
|
+
FP では純粋関数が多いため
|
|
628
|
+
単体テストが非常に効果的
|
|
629
|
+
end note
|
|
630
|
+
|
|
631
|
+
@enduml
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
---
|
|
635
|
+
|
|
636
|
+
## まとめ
|
|
637
|
+
|
|
638
|
+
### Part VI で学んだこと
|
|
639
|
+
|
|
640
|
+
```plantuml
|
|
641
|
+
@startuml
|
|
642
|
+
!theme plain
|
|
643
|
+
|
|
644
|
+
rectangle "Part VI: 実践的なアプリケーション" {
|
|
645
|
+
rectangle "第11章" as ch11 {
|
|
646
|
+
card "ドメインモデル設計"
|
|
647
|
+
card "IDataAccess 抽象化"
|
|
648
|
+
card "Resource 管理"
|
|
649
|
+
card "キャッシュ実装"
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
rectangle "第12章" as ch12 {
|
|
653
|
+
card "テスト用スタブ"
|
|
654
|
+
card "単体テスト"
|
|
655
|
+
card "統合テスト"
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
ch11 --> ch12
|
|
660
|
+
|
|
661
|
+
@enduml
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
### キーポイント
|
|
665
|
+
|
|
666
|
+
1. **抽象化の重要性**: IDataAccess インターフェースで外部依存を抽象化
|
|
667
|
+
2. **Resource でリソース管理**: 安全なリソースの取得と解放
|
|
668
|
+
3. **Ref でキャッシュ**: スレッドセーフな状態管理
|
|
669
|
+
4. **Option でエラー処理**: 明示的なエラーハンドリング
|
|
670
|
+
5. **スタブ**: 外部依存を差し替えてテスト
|
|
671
|
+
6. **record 型**: イミュータブルなドメインモデル
|
|
672
|
+
|
|
673
|
+
### 学習の総括
|
|
674
|
+
|
|
675
|
+
```plantuml
|
|
676
|
+
@startuml
|
|
677
|
+
!theme plain
|
|
678
|
+
left to right direction
|
|
679
|
+
|
|
680
|
+
rectangle "C# FP の学習パス" {
|
|
681
|
+
card "Part I\n基礎" as p1
|
|
682
|
+
card "Part II\n関数型スタイル" as p2
|
|
683
|
+
card "Part III\nエラーハンドリング" as p3
|
|
684
|
+
card "Part IV\n非同期/ストリーム" as p4
|
|
685
|
+
card "Part V\n並行処理" as p5
|
|
686
|
+
card "Part VI\n実践" as p6
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
p1 --> p2
|
|
690
|
+
p2 --> p3
|
|
691
|
+
p3 --> p4
|
|
692
|
+
p4 --> p5
|
|
693
|
+
p5 --> p6
|
|
694
|
+
|
|
695
|
+
@enduml
|
|
696
|
+
```
|
|
697
|
+
|
|
698
|
+
---
|
|
699
|
+
|
|
700
|
+
## シリーズ全体の総括
|
|
701
|
+
|
|
702
|
+
本シリーズでは、「Grokking Functional Programming」の内容に沿って、関数型プログラミングの基礎から実践的なアプリケーション構築までを C# と LanguageExt ライブラリで学びました。
|
|
703
|
+
|
|
704
|
+
### 学んだ主な概念
|
|
705
|
+
|
|
706
|
+
| Part | 章 | 主な概念 |
|
|
707
|
+
|------|-----|----------|
|
|
708
|
+
| I | 1-2 | 純粋関数、参照透過性 |
|
|
709
|
+
| II | 3-5 | イミュータブルデータ、高階関数、Bind |
|
|
710
|
+
| III | 6-7 | Option、Either/Fin |
|
|
711
|
+
| IV | 8-9 | Task(非同期)、IAsyncEnumerable(ストリーム) |
|
|
712
|
+
| V | 10 | 並行処理、Ref、Agent |
|
|
713
|
+
| VI | 11 | 実践アプリケーション、テスト |
|
|
714
|
+
|
|
715
|
+
### 関数型プログラミングの利点
|
|
716
|
+
|
|
717
|
+
1. **予測可能性**: 純粋関数は同じ入力に対して常に同じ出力
|
|
718
|
+
2. **テスト容易性**: 副作用がないためテストが簡単
|
|
719
|
+
3. **合成可能性**: 小さな関数を組み合わせて複雑な処理を構築
|
|
720
|
+
4. **並行安全性**: イミュータブルデータは競合状態を防ぐ
|
|
721
|
+
5. **型安全性**: Option、Either で null や例外を型で表現
|
|
722
|
+
|
|
723
|
+
### C# + LanguageExt の特徴
|
|
724
|
+
|
|
725
|
+
C# は .NET プラットフォーム上で動作するマルチパラダイム言語で、LanguageExt ライブラリと組み合わせることで以下の特徴を持ちます:
|
|
726
|
+
|
|
727
|
+
- **Option<T>**: null 安全なオプション型
|
|
728
|
+
- **Either<L, R>**: 成功/失敗を型で表現
|
|
729
|
+
- **Seq<T>**: イミュータブルなシーケンス
|
|
730
|
+
- **Map<K, V>**: イミュータブルな辞書
|
|
731
|
+
- **パターンマッチング**: switch 式で関数型スタイルの分岐
|
|
732
|
+
- **record 型**: イミュータブルなデータ型
|
|
733
|
+
|
|
734
|
+
### 次のステップ
|
|
735
|
+
|
|
736
|
+
- C# の高度な機能(Source Generators、拡張メソッド)を学ぶ
|
|
737
|
+
- ASP.NET Core で FP スタイルの Web API を構築
|
|
738
|
+
- より高度な LanguageExt 機能(Eff, Aff)を探索
|
|
739
|
+
- 実際のプロジェクトで FP を適用する
|