@k2works/claude-code-booster 6.1.0 → 6.3.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.
Files changed (622) hide show
  1. package/lib/assets/.claude/skills/practicing-getting-start-tdd/SKILL.md +3 -7
  2. package/lib/assets/docs/article/index.md +16 -41
  3. package/lib/assets/docs/assets/js/extra.js +5 -1
  4. package/lib/assets/docs/assets/js/mathjax.js +27 -0
  5. package/lib/assets/docs/index.md +1 -1
  6. package/lib/assets/mkdocs.yml +6 -7
  7. package/lib/assets/ops/docker/mkdoc/Dockerfile +19 -19
  8. package/lib/assets/package.json +1 -1
  9. package/package.json +1 -1
  10. package/lib/assets/docs/article/functional-desgin-ppp/all/01-immutability-and-data-transformation.md +0 -484
  11. package/lib/assets/docs/article/functional-desgin-ppp/all/02-function-composition.md +0 -528
  12. package/lib/assets/docs/article/functional-desgin-ppp/all/03-polymorphism.md +0 -546
  13. package/lib/assets/docs/article/functional-desgin-ppp/all/04-data-validation.md +0 -309
  14. package/lib/assets/docs/article/functional-desgin-ppp/all/05-property-based-testing.md +0 -329
  15. package/lib/assets/docs/article/functional-desgin-ppp/all/06-tdd-and-functional.md +0 -507
  16. package/lib/assets/docs/article/functional-desgin-ppp/all/07-composite-pattern.md +0 -307
  17. package/lib/assets/docs/article/functional-desgin-ppp/all/08-decorator-pattern.md +0 -300
  18. package/lib/assets/docs/article/functional-desgin-ppp/all/09-adapter-pattern.md +0 -345
  19. package/lib/assets/docs/article/functional-desgin-ppp/all/10-strategy-pattern.md +0 -312
  20. package/lib/assets/docs/article/functional-desgin-ppp/all/11-command-pattern.md +0 -295
  21. package/lib/assets/docs/article/functional-desgin-ppp/all/12-visitor-pattern.md +0 -331
  22. package/lib/assets/docs/article/functional-desgin-ppp/all/13-abstract-factory-pattern.md +0 -328
  23. package/lib/assets/docs/article/functional-desgin-ppp/all/14-abstract-server-pattern.md +0 -374
  24. package/lib/assets/docs/article/functional-desgin-ppp/all/15-gossiping-bus-drivers.md +0 -165
  25. package/lib/assets/docs/article/functional-desgin-ppp/all/16-payroll-system.md +0 -187
  26. package/lib/assets/docs/article/functional-desgin-ppp/all/17-video-rental-system.md +0 -321
  27. package/lib/assets/docs/article/functional-desgin-ppp/all/18-concurrency-system.md +0 -296
  28. package/lib/assets/docs/article/functional-desgin-ppp/all/19-wa-tor-simulation.md +0 -295
  29. package/lib/assets/docs/article/functional-desgin-ppp/all/20-pattern-interactions.md +0 -283
  30. package/lib/assets/docs/article/functional-desgin-ppp/all/21-best-practices.md +0 -303
  31. package/lib/assets/docs/article/functional-desgin-ppp/all/22-oo-to-fp-migration.md +0 -346
  32. package/lib/assets/docs/article/functional-desgin-ppp/all/index.md +0 -388
  33. package/lib/assets/docs/article/functional-desgin-ppp/clojure/01-immutability-and-data-transformation.md +0 -282
  34. package/lib/assets/docs/article/functional-desgin-ppp/clojure/02-function-composition.md +0 -389
  35. package/lib/assets/docs/article/functional-desgin-ppp/clojure/03-polymorphism.md +0 -393
  36. package/lib/assets/docs/article/functional-desgin-ppp/clojure/04-clojure-spec.md +0 -359
  37. package/lib/assets/docs/article/functional-desgin-ppp/clojure/05-property-based-testing.md +0 -361
  38. package/lib/assets/docs/article/functional-desgin-ppp/clojure/06-tdd-in-functional.md +0 -392
  39. package/lib/assets/docs/article/functional-desgin-ppp/clojure/07-composite-pattern.md +0 -538
  40. package/lib/assets/docs/article/functional-desgin-ppp/clojure/08-decorator-pattern.md +0 -404
  41. package/lib/assets/docs/article/functional-desgin-ppp/clojure/09-adapter-pattern.md +0 -408
  42. package/lib/assets/docs/article/functional-desgin-ppp/clojure/10-strategy-pattern.md +0 -494
  43. package/lib/assets/docs/article/functional-desgin-ppp/clojure/11-command-pattern.md +0 -575
  44. package/lib/assets/docs/article/functional-desgin-ppp/clojure/12-visitor-pattern.md +0 -576
  45. package/lib/assets/docs/article/functional-desgin-ppp/clojure/13-abstract-factory-pattern.md +0 -484
  46. package/lib/assets/docs/article/functional-desgin-ppp/clojure/14-abstract-server-pattern.md +0 -471
  47. package/lib/assets/docs/article/functional-desgin-ppp/clojure/15-gossiping-bus-drivers.md +0 -334
  48. package/lib/assets/docs/article/functional-desgin-ppp/clojure/16-payroll-system.md +0 -410
  49. package/lib/assets/docs/article/functional-desgin-ppp/clojure/17-video-rental-system.md +0 -459
  50. package/lib/assets/docs/article/functional-desgin-ppp/clojure/18-concurrency-system.md +0 -484
  51. package/lib/assets/docs/article/functional-desgin-ppp/clojure/19-wator-simulation.md +0 -748
  52. package/lib/assets/docs/article/functional-desgin-ppp/clojure/20-pattern-interactions.md +0 -576
  53. package/lib/assets/docs/article/functional-desgin-ppp/clojure/21-best-practices.md +0 -527
  54. package/lib/assets/docs/article/functional-desgin-ppp/clojure/22-oo-to-fp-migration.md +0 -541
  55. package/lib/assets/docs/article/functional-desgin-ppp/clojure/index.md +0 -241
  56. package/lib/assets/docs/article/functional-desgin-ppp/elixir/01-immutability-and-data-transformation.md +0 -392
  57. package/lib/assets/docs/article/functional-desgin-ppp/elixir/02-function-composition.md +0 -383
  58. package/lib/assets/docs/article/functional-desgin-ppp/elixir/03-polymorphism.md +0 -384
  59. package/lib/assets/docs/article/functional-desgin-ppp/elixir/04-data-validation.md +0 -204
  60. package/lib/assets/docs/article/functional-desgin-ppp/elixir/05-property-based-testing.md +0 -277
  61. package/lib/assets/docs/article/functional-desgin-ppp/elixir/06-tdd-and-fp.md +0 -303
  62. package/lib/assets/docs/article/functional-desgin-ppp/elixir/07-effects-and-pure-functions.md +0 -173
  63. package/lib/assets/docs/article/functional-desgin-ppp/elixir/08-error-handling-strategies.md +0 -177
  64. package/lib/assets/docs/article/functional-desgin-ppp/elixir/09-io-and-external-systems.md +0 -263
  65. package/lib/assets/docs/article/functional-desgin-ppp/elixir/10-concurrency-patterns.md +0 -278
  66. package/lib/assets/docs/article/functional-desgin-ppp/elixir/11-command-pattern.md +0 -157
  67. package/lib/assets/docs/article/functional-desgin-ppp/elixir/12-visitor-pattern.md +0 -185
  68. package/lib/assets/docs/article/functional-desgin-ppp/elixir/13-abstract-factory-pattern.md +0 -613
  69. package/lib/assets/docs/article/functional-desgin-ppp/elixir/14-abstract-server-pattern.md +0 -738
  70. package/lib/assets/docs/article/functional-desgin-ppp/elixir/15-gossiping-bus-drivers.md +0 -300
  71. package/lib/assets/docs/article/functional-desgin-ppp/elixir/16-payroll-system.md +0 -429
  72. package/lib/assets/docs/article/functional-desgin-ppp/elixir/17-video-rental-system.md +0 -328
  73. package/lib/assets/docs/article/functional-desgin-ppp/elixir/18-concurrency-system.md +0 -475
  74. package/lib/assets/docs/article/functional-desgin-ppp/elixir/19-wator-simulation.md +0 -532
  75. package/lib/assets/docs/article/functional-desgin-ppp/elixir/20-pattern-interactions.md +0 -296
  76. package/lib/assets/docs/article/functional-desgin-ppp/elixir/21-best-practices.md +0 -349
  77. package/lib/assets/docs/article/functional-desgin-ppp/elixir/22-oo-to-fp-migration.md +0 -404
  78. package/lib/assets/docs/article/functional-desgin-ppp/elixir/index.md +0 -248
  79. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/01-immutability-and-data-transformation.md +0 -393
  80. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/02-function-composition.md +0 -461
  81. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/03-polymorphism.md +0 -504
  82. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/04-data-validation.md +0 -425
  83. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/05-property-based-testing.md +0 -391
  84. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/06-tdd-functional.md +0 -696
  85. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/07-composite-pattern.md +0 -451
  86. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/08-decorator-pattern.md +0 -488
  87. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/09-adapter-pattern.md +0 -488
  88. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/10-strategy-pattern.md +0 -436
  89. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/11-command-pattern.md +0 -437
  90. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/12-visitor-pattern.md +0 -348
  91. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/13-abstract-factory-pattern.md +0 -318
  92. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/14-abstract-server-pattern.md +0 -605
  93. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/15-gossiping-bus-drivers.md +0 -364
  94. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/16-payroll-system.md +0 -359
  95. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/17-video-rental-system.md +0 -423
  96. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/18-concurrency-system.md +0 -376
  97. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/19-wator-simulation.md +0 -412
  98. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/20-pattern-interactions.md +0 -300
  99. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/21-best-practices.md +0 -333
  100. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/22-oo-to-fp-migration.md +0 -341
  101. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/index.md +0 -274
  102. package/lib/assets/docs/article/functional-desgin-ppp/haskell/01-immutability-and-data-transformation.md +0 -307
  103. package/lib/assets/docs/article/functional-desgin-ppp/haskell/02-function-composition.md +0 -313
  104. package/lib/assets/docs/article/functional-desgin-ppp/haskell/03-polymorphism.md +0 -371
  105. package/lib/assets/docs/article/functional-desgin-ppp/haskell/04-data-validation.md +0 -266
  106. package/lib/assets/docs/article/functional-desgin-ppp/haskell/05-property-based-testing.md +0 -263
  107. package/lib/assets/docs/article/functional-desgin-ppp/haskell/06-tdd-functional.md +0 -292
  108. package/lib/assets/docs/article/functional-desgin-ppp/haskell/07-composite-pattern.md +0 -404
  109. package/lib/assets/docs/article/functional-desgin-ppp/haskell/08-decorator-pattern.md +0 -328
  110. package/lib/assets/docs/article/functional-desgin-ppp/haskell/09-adapter-pattern.md +0 -391
  111. package/lib/assets/docs/article/functional-desgin-ppp/haskell/10-strategy-pattern.md +0 -296
  112. package/lib/assets/docs/article/functional-desgin-ppp/haskell/11-command-pattern.md +0 -312
  113. package/lib/assets/docs/article/functional-desgin-ppp/haskell/12-visitor-pattern.md +0 -335
  114. package/lib/assets/docs/article/functional-desgin-ppp/haskell/13-abstract-factory-pattern.md +0 -341
  115. package/lib/assets/docs/article/functional-desgin-ppp/haskell/14-abstract-server-pattern.md +0 -388
  116. package/lib/assets/docs/article/functional-desgin-ppp/haskell/15-gossiping-bus-drivers.md +0 -186
  117. package/lib/assets/docs/article/functional-desgin-ppp/haskell/16-payroll-system.md +0 -228
  118. package/lib/assets/docs/article/functional-desgin-ppp/haskell/17-video-rental-system.md +0 -253
  119. package/lib/assets/docs/article/functional-desgin-ppp/haskell/18-concurrency-system.md +0 -372
  120. package/lib/assets/docs/article/functional-desgin-ppp/haskell/19-wator-simulation.md +0 -447
  121. package/lib/assets/docs/article/functional-desgin-ppp/haskell/20-pattern-interactions.md +0 -334
  122. package/lib/assets/docs/article/functional-desgin-ppp/haskell/21-best-practices.md +0 -412
  123. package/lib/assets/docs/article/functional-desgin-ppp/haskell/22-oo-to-fp-migration.md +0 -478
  124. package/lib/assets/docs/article/functional-desgin-ppp/haskell/index.md +0 -174
  125. package/lib/assets/docs/article/functional-desgin-ppp/index.md +0 -90
  126. package/lib/assets/docs/article/functional-desgin-ppp/rust/01-immutability-and-data-transformation.md +0 -459
  127. package/lib/assets/docs/article/functional-desgin-ppp/rust/02-function-composition.md +0 -472
  128. package/lib/assets/docs/article/functional-desgin-ppp/rust/03-polymorphism.md +0 -434
  129. package/lib/assets/docs/article/functional-desgin-ppp/rust/04-data-validation.md +0 -282
  130. package/lib/assets/docs/article/functional-desgin-ppp/rust/05-property-based-testing.md +0 -256
  131. package/lib/assets/docs/article/functional-desgin-ppp/rust/06-tdd-and-functional.md +0 -850
  132. package/lib/assets/docs/article/functional-desgin-ppp/rust/07-composite-pattern.md +0 -393
  133. package/lib/assets/docs/article/functional-desgin-ppp/rust/08-decorator-pattern.md +0 -392
  134. package/lib/assets/docs/article/functional-desgin-ppp/rust/09-adapter-pattern.md +0 -348
  135. package/lib/assets/docs/article/functional-desgin-ppp/rust/10-strategy-pattern.md +0 -340
  136. package/lib/assets/docs/article/functional-desgin-ppp/rust/11-command-pattern.md +0 -365
  137. package/lib/assets/docs/article/functional-desgin-ppp/rust/12-visitor-pattern.md +0 -388
  138. package/lib/assets/docs/article/functional-desgin-ppp/rust/13-abstract-factory-pattern.md +0 -370
  139. package/lib/assets/docs/article/functional-desgin-ppp/rust/14-abstract-server-pattern.md +0 -401
  140. package/lib/assets/docs/article/functional-desgin-ppp/rust/15-gossiping-bus-drivers.md +0 -309
  141. package/lib/assets/docs/article/functional-desgin-ppp/rust/16-payroll-system.md +0 -306
  142. package/lib/assets/docs/article/functional-desgin-ppp/rust/17-video-rental-system.md +0 -313
  143. package/lib/assets/docs/article/functional-desgin-ppp/rust/18-concurrency-system.md +0 -324
  144. package/lib/assets/docs/article/functional-desgin-ppp/rust/19-wator-simulation.md +0 -320
  145. package/lib/assets/docs/article/functional-desgin-ppp/rust/20-pattern-interactions.md +0 -313
  146. package/lib/assets/docs/article/functional-desgin-ppp/rust/21-best-practices.md +0 -345
  147. package/lib/assets/docs/article/functional-desgin-ppp/rust/22-oo-to-fp-migration.md +0 -358
  148. package/lib/assets/docs/article/functional-desgin-ppp/rust/index.md +0 -243
  149. package/lib/assets/docs/article/functional-desgin-ppp/scala/01-immutability-and-data-transformation.md +0 -337
  150. package/lib/assets/docs/article/functional-desgin-ppp/scala/02-function-composition.md +0 -357
  151. package/lib/assets/docs/article/functional-desgin-ppp/scala/03-polymorphism.md +0 -366
  152. package/lib/assets/docs/article/functional-desgin-ppp/scala/04-data-validation.md +0 -373
  153. package/lib/assets/docs/article/functional-desgin-ppp/scala/05-property-based-testing.md +0 -524
  154. package/lib/assets/docs/article/functional-desgin-ppp/scala/06-tdd-functional.md +0 -566
  155. package/lib/assets/docs/article/functional-desgin-ppp/scala/07-composite-pattern.md +0 -372
  156. package/lib/assets/docs/article/functional-desgin-ppp/scala/08-decorator-pattern.md +0 -336
  157. package/lib/assets/docs/article/functional-desgin-ppp/scala/09-adapter-pattern.md +0 -526
  158. package/lib/assets/docs/article/functional-desgin-ppp/scala/10-strategy-pattern.md +0 -450
  159. package/lib/assets/docs/article/functional-desgin-ppp/scala/11-command-pattern.md +0 -416
  160. package/lib/assets/docs/article/functional-desgin-ppp/scala/12-visitor-pattern.md +0 -388
  161. package/lib/assets/docs/article/functional-desgin-ppp/scala/13-abstract-factory-pattern.md +0 -407
  162. package/lib/assets/docs/article/functional-desgin-ppp/scala/14-abstract-server-pattern.md +0 -485
  163. package/lib/assets/docs/article/functional-desgin-ppp/scala/15-gossiping-bus-drivers.md +0 -400
  164. package/lib/assets/docs/article/functional-desgin-ppp/scala/16-payroll-system.md +0 -351
  165. package/lib/assets/docs/article/functional-desgin-ppp/scala/17-video-rental-system.md +0 -333
  166. package/lib/assets/docs/article/functional-desgin-ppp/scala/18-concurrency-system.md +0 -739
  167. package/lib/assets/docs/article/functional-desgin-ppp/scala/19-wator-simulation.md +0 -633
  168. package/lib/assets/docs/article/functional-desgin-ppp/scala/20-pattern-interactions.md +0 -521
  169. package/lib/assets/docs/article/functional-desgin-ppp/scala/21-best-practices.md +0 -442
  170. package/lib/assets/docs/article/functional-desgin-ppp/scala/22-oo-to-fp-migration.md +0 -697
  171. package/lib/assets/docs/article/functional-desgin-ppp/scala/index.md +0 -243
  172. package/lib/assets/docs/article/getting-start-docker-kubernetes/01-container-and-docker-basics.md +0 -341
  173. package/lib/assets/docs/article/getting-start-docker-kubernetes/02-container-deployment.md +0 -785
  174. package/lib/assets/docs/article/getting-start-docker-kubernetes/03-practical-container-build-deploy.md +0 -466
  175. package/lib/assets/docs/article/getting-start-docker-kubernetes/04-multi-container-application.md +0 -878
  176. package/lib/assets/docs/article/getting-start-docker-kubernetes/05-kubernetes-introduction.md +0 -585
  177. package/lib/assets/docs/article/getting-start-docker-kubernetes/06-kubernetes-deploy-cluster.md +0 -620
  178. package/lib/assets/docs/article/getting-start-docker-kubernetes/07-kubernetes-advanced.md +0 -621
  179. package/lib/assets/docs/article/getting-start-docker-kubernetes/08-kubernetes-packaging.md +0 -518
  180. package/lib/assets/docs/article/getting-start-docker-kubernetes/09-container-operations.md +0 -546
  181. package/lib/assets/docs/article/getting-start-docker-kubernetes/10-optimal-container-image.md +0 -640
  182. package/lib/assets/docs/article/getting-start-docker-kubernetes/11-continuous-delivery.md +0 -607
  183. package/lib/assets/docs/article/getting-start-docker-kubernetes/12-container-use-cases.md +0 -463
  184. package/lib/assets/docs/article/getting-start-docker-kubernetes/13-case-monolith-compose-vs-kustomize.md +0 -346
  185. package/lib/assets/docs/article/getting-start-docker-kubernetes/14-case-event-driven-kustomize-vs-helm.md +0 -324
  186. package/lib/assets/docs/article/getting-start-docker-kubernetes/15-case-escqrs-axon-kustomize-vs-helm.md +0 -297
  187. package/lib/assets/docs/article/getting-start-docker-kubernetes/16-case-escqrs-kafka-kustomize-vs-helm.md +0 -256
  188. package/lib/assets/docs/article/getting-start-docker-kubernetes/17-case-comparison-summary.md +0 -141
  189. package/lib/assets/docs/article/getting-start-docker-kubernetes/18-case-load-testing.md +0 -303
  190. package/lib/assets/docs/article/getting-start-docker-kubernetes/appendix-a-dev-tools-setup.md +0 -274
  191. package/lib/assets/docs/article/getting-start-docker-kubernetes/appendix-b-orchestration-environments.md +0 -530
  192. package/lib/assets/docs/article/getting-start-docker-kubernetes/appendix-c-tips.md +0 -519
  193. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-01-comic.png +0 -0
  194. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-02-comic.png +0 -0
  195. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-03-comic.png +0 -0
  196. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-04-comic.png +0 -0
  197. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-05-comic.png +0 -0
  198. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-06-comic.png +0 -0
  199. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-07-comic.png +0 -0
  200. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-08-comic.png +0 -0
  201. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-09-comic.png +0 -0
  202. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-10-comic.png +0 -0
  203. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-11-comic.png +0 -0
  204. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-12-comic.png +0 -0
  205. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/overview-comic.png +0 -0
  206. package/lib/assets/docs/article/getting-start-docker-kubernetes/index.md +0 -82
  207. package/lib/assets/docs/article/getting-start-docker-kubernetes/outline.md +0 -220
  208. package/lib/assets/docs/article/getting-start-tdd/clojure/01-todo-list-and-first-test.md +0 -175
  209. package/lib/assets/docs/article/getting-start-tdd/clojure/02-fake-it-and-triangulation.md +0 -171
  210. package/lib/assets/docs/article/getting-start-tdd/clojure/03-obvious-implementation-and-refactoring.md +0 -144
  211. package/lib/assets/docs/article/getting-start-tdd/clojure/04-version-control-and-conventional-commits.md +0 -97
  212. package/lib/assets/docs/article/getting-start-tdd/clojure/05-package-management-and-static-analysis.md +0 -308
  213. package/lib/assets/docs/article/getting-start-tdd/clojure/06-task-runner-and-ci-cd.md +0 -250
  214. package/lib/assets/docs/article/getting-start-tdd/clojure/07-protocols-and-records.md +0 -140
  215. package/lib/assets/docs/article/getting-start-tdd/clojure/08-multimethods-and-design-patterns.md +0 -139
  216. package/lib/assets/docs/article/getting-start-tdd/clojure/09-namespaces-and-module-design.md +0 -136
  217. package/lib/assets/docs/article/getting-start-tdd/clojure/10-higher-order-functions-and-composition.md +0 -123
  218. package/lib/assets/docs/article/getting-start-tdd/clojure/11-persistent-data-and-pipeline.md +0 -147
  219. package/lib/assets/docs/article/getting-start-tdd/clojure/12-error-handling-and-spec.md +0 -170
  220. package/lib/assets/docs/article/getting-start-tdd/clojure/index.md +0 -65
  221. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter01.md +0 -241
  222. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter02.md +0 -253
  223. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter03.md +0 -211
  224. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter04.md +0 -101
  225. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter05.md +0 -265
  226. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter06.md +0 -204
  227. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter07.md +0 -223
  228. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter08.md +0 -258
  229. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter09.md +0 -183
  230. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter10.md +0 -175
  231. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter11.md +0 -201
  232. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter12.md +0 -220
  233. package/lib/assets/docs/article/getting-start-tdd/csharp/index.md +0 -83
  234. package/lib/assets/docs/article/getting-start-tdd/elixir/01-todo-list-and-first-test.md +0 -96
  235. package/lib/assets/docs/article/getting-start-tdd/elixir/02-fake-it-and-triangulation.md +0 -104
  236. package/lib/assets/docs/article/getting-start-tdd/elixir/03-obvious-implementation-and-refactoring.md +0 -118
  237. package/lib/assets/docs/article/getting-start-tdd/elixir/04-version-control-and-conventional-commits.md +0 -105
  238. package/lib/assets/docs/article/getting-start-tdd/elixir/05-package-management-and-static-analysis.md +0 -97
  239. package/lib/assets/docs/article/getting-start-tdd/elixir/06-task-runner-and-ci-cd.md +0 -80
  240. package/lib/assets/docs/article/getting-start-tdd/elixir/07-structs-and-protocols.md +0 -119
  241. package/lib/assets/docs/article/getting-start-tdd/elixir/08-pattern-matching-and-guards.md +0 -117
  242. package/lib/assets/docs/article/getting-start-tdd/elixir/09-module-design-and-behaviours.md +0 -113
  243. package/lib/assets/docs/article/getting-start-tdd/elixir/10-higher-order-functions-and-pipeline.md +0 -187
  244. package/lib/assets/docs/article/getting-start-tdd/elixir/11-stream-and-lazy-evaluation.md +0 -151
  245. package/lib/assets/docs/article/getting-start-tdd/elixir/12-error-handling-and-with.md +0 -154
  246. package/lib/assets/docs/article/getting-start-tdd/elixir/index.md +0 -35
  247. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter01.md +0 -211
  248. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter02.md +0 -255
  249. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter03.md +0 -227
  250. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter04.md +0 -188
  251. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter05.md +0 -276
  252. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter06.md +0 -199
  253. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter07.md +0 -170
  254. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter08.md +0 -184
  255. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter09.md +0 -231
  256. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter10.md +0 -198
  257. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter11.md +0 -221
  258. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter12.md +0 -224
  259. package/lib/assets/docs/article/getting-start-tdd/fsharp/index.md +0 -71
  260. package/lib/assets/docs/article/getting-start-tdd/go/01-todo-list-and-first-test.md +0 -222
  261. package/lib/assets/docs/article/getting-start-tdd/go/02-fake-it-and-triangulation.md +0 -311
  262. package/lib/assets/docs/article/getting-start-tdd/go/03-obvious-implementation-and-refactoring.md +0 -348
  263. package/lib/assets/docs/article/getting-start-tdd/go/04-version-control-and-conventional-commits.md +0 -121
  264. package/lib/assets/docs/article/getting-start-tdd/go/05-package-management-and-static-analysis.md +0 -281
  265. package/lib/assets/docs/article/getting-start-tdd/go/06-task-runner-and-ci-cd.md +0 -242
  266. package/lib/assets/docs/article/getting-start-tdd/go/07-encapsulation-and-polymorphism.md +0 -402
  267. package/lib/assets/docs/article/getting-start-tdd/go/08-design-patterns.md +0 -430
  268. package/lib/assets/docs/article/getting-start-tdd/go/09-solid-principles-and-module-design.md +0 -408
  269. package/lib/assets/docs/article/getting-start-tdd/go/10-higher-order-functions-and-composition.md +0 -234
  270. package/lib/assets/docs/article/getting-start-tdd/go/11-immutable-data-and-pipeline.md +0 -304
  271. package/lib/assets/docs/article/getting-start-tdd/go/12-error-handling-and-type-safety.md +0 -419
  272. package/lib/assets/docs/article/getting-start-tdd/go/index.md +0 -83
  273. package/lib/assets/docs/article/getting-start-tdd/haskell/01-todo-list-and-first-test.md +0 -288
  274. package/lib/assets/docs/article/getting-start-tdd/haskell/02-fake-it-and-triangulation.md +0 -346
  275. package/lib/assets/docs/article/getting-start-tdd/haskell/03-obvious-implementation-and-refactoring.md +0 -266
  276. package/lib/assets/docs/article/getting-start-tdd/haskell/04-version-control-and-conventional-commits.md +0 -191
  277. package/lib/assets/docs/article/getting-start-tdd/haskell/05-package-management-and-static-analysis.md +0 -322
  278. package/lib/assets/docs/article/getting-start-tdd/haskell/06-task-runner-and-ci-cd.md +0 -318
  279. package/lib/assets/docs/article/getting-start-tdd/haskell/07-algebraic-data-types-and-type-classes.md +0 -421
  280. package/lib/assets/docs/article/getting-start-tdd/haskell/08-pattern-matching-and-guards.md +0 -399
  281. package/lib/assets/docs/article/getting-start-tdd/haskell/09-module-design-and-smart-constructors.md +0 -470
  282. package/lib/assets/docs/article/getting-start-tdd/haskell/10-higher-order-functions-and-currying.md +0 -443
  283. package/lib/assets/docs/article/getting-start-tdd/haskell/11-function-composition-and-point-free.md +0 -401
  284. package/lib/assets/docs/article/getting-start-tdd/haskell/12-monad-and-error-handling.md +0 -640
  285. package/lib/assets/docs/article/getting-start-tdd/haskell/index.md +0 -49
  286. package/lib/assets/docs/article/getting-start-tdd/index.md +0 -93
  287. package/lib/assets/docs/article/getting-start-tdd/integration/01-language-overview.md +0 -384
  288. package/lib/assets/docs/article/getting-start-tdd/integration/02-test-framework-comparison.md +0 -358
  289. package/lib/assets/docs/article/getting-start-tdd/integration/03-tdd-pattern-comparison.md +0 -454
  290. package/lib/assets/docs/article/getting-start-tdd/integration/04-type-system-comparison.md +0 -418
  291. package/lib/assets/docs/article/getting-start-tdd/integration/05-dev-environment-comparison.md +0 -339
  292. package/lib/assets/docs/article/getting-start-tdd/integration/06-learning-roadmap.md +0 -299
  293. package/lib/assets/docs/article/getting-start-tdd/integration/index.md +0 -69
  294. package/lib/assets/docs/article/getting-start-tdd/java/01-todo-list-and-first-test.md +0 -243
  295. package/lib/assets/docs/article/getting-start-tdd/java/02-fake-it-and-triangulation.md +0 -270
  296. package/lib/assets/docs/article/getting-start-tdd/java/03-obvious-implementation-and-refactoring.md +0 -194
  297. package/lib/assets/docs/article/getting-start-tdd/java/04-version-control-and-conventional-commits.md +0 -124
  298. package/lib/assets/docs/article/getting-start-tdd/java/05-package-management-and-static-analysis.md +0 -391
  299. package/lib/assets/docs/article/getting-start-tdd/java/06-task-runner-and-ci-cd.md +0 -281
  300. package/lib/assets/docs/article/getting-start-tdd/java/07-encapsulation-and-polymorphism.md +0 -635
  301. package/lib/assets/docs/article/getting-start-tdd/java/08-design-patterns.md +0 -402
  302. package/lib/assets/docs/article/getting-start-tdd/java/09-solid-principles-and-module-design.md +0 -319
  303. package/lib/assets/docs/article/getting-start-tdd/java/10-higher-order-functions-and-composition.md +0 -197
  304. package/lib/assets/docs/article/getting-start-tdd/java/11-immutable-data-and-pipeline.md +0 -176
  305. package/lib/assets/docs/article/getting-start-tdd/java/12-error-handling-and-type-safety.md +0 -214
  306. package/lib/assets/docs/article/getting-start-tdd/java/index.md +0 -61
  307. package/lib/assets/docs/article/getting-start-tdd/node/01-todo-list-and-first-test.md +0 -253
  308. package/lib/assets/docs/article/getting-start-tdd/node/02-fake-it-and-triangulation.md +0 -271
  309. package/lib/assets/docs/article/getting-start-tdd/node/03-obvious-implementation-and-refactoring.md +0 -178
  310. package/lib/assets/docs/article/getting-start-tdd/node/04-version-control-and-conventional-commits.md +0 -121
  311. package/lib/assets/docs/article/getting-start-tdd/node/05-package-management-and-static-analysis.md +0 -323
  312. package/lib/assets/docs/article/getting-start-tdd/node/06-task-runner-and-ci-cd.md +0 -244
  313. package/lib/assets/docs/article/getting-start-tdd/node/07-encapsulation-and-polymorphism.md +0 -336
  314. package/lib/assets/docs/article/getting-start-tdd/node/08-design-patterns.md +0 -331
  315. package/lib/assets/docs/article/getting-start-tdd/node/09-solid-principles-and-module-design.md +0 -294
  316. package/lib/assets/docs/article/getting-start-tdd/node/10-higher-order-functions-and-composition.md +0 -208
  317. package/lib/assets/docs/article/getting-start-tdd/node/11-immutable-data-and-pipeline.md +0 -216
  318. package/lib/assets/docs/article/getting-start-tdd/node/12-error-handling-and-type-safety.md +0 -304
  319. package/lib/assets/docs/article/getting-start-tdd/node/index.md +0 -56
  320. package/lib/assets/docs/article/getting-start-tdd/php/01-todo-list-and-first-test.md +0 -268
  321. package/lib/assets/docs/article/getting-start-tdd/php/02-fake-it-and-triangulation.md +0 -209
  322. package/lib/assets/docs/article/getting-start-tdd/php/03-obvious-implementation-and-refactoring.md +0 -257
  323. package/lib/assets/docs/article/getting-start-tdd/php/04-version-control-and-conventional-commits.md +0 -150
  324. package/lib/assets/docs/article/getting-start-tdd/php/05-package-management-and-static-analysis.md +0 -419
  325. package/lib/assets/docs/article/getting-start-tdd/php/06-task-runner-and-ci-cd.md +0 -330
  326. package/lib/assets/docs/article/getting-start-tdd/php/07-encapsulation-and-polymorphism.md +0 -380
  327. package/lib/assets/docs/article/getting-start-tdd/php/08-design-patterns.md +0 -461
  328. package/lib/assets/docs/article/getting-start-tdd/php/09-solid-principles-and-module-design.md +0 -468
  329. package/lib/assets/docs/article/getting-start-tdd/php/10-higher-order-functions-and-composition.md +0 -190
  330. package/lib/assets/docs/article/getting-start-tdd/php/11-immutable-data-and-pipeline.md +0 -274
  331. package/lib/assets/docs/article/getting-start-tdd/php/12-error-handling-and-type-safety.md +0 -316
  332. package/lib/assets/docs/article/getting-start-tdd/php/index.md +0 -84
  333. package/lib/assets/docs/article/getting-start-tdd/python/01-todo-list-and-first-test.md +0 -210
  334. package/lib/assets/docs/article/getting-start-tdd/python/02-fake-it-and-triangulation.md +0 -256
  335. package/lib/assets/docs/article/getting-start-tdd/python/03-obvious-implementation-and-refactoring.md +0 -208
  336. package/lib/assets/docs/article/getting-start-tdd/python/04-version-control-and-conventional-commits.md +0 -96
  337. package/lib/assets/docs/article/getting-start-tdd/python/05-package-management-and-static-analysis.md +0 -283
  338. package/lib/assets/docs/article/getting-start-tdd/python/06-task-runner-and-ci-cd.md +0 -199
  339. package/lib/assets/docs/article/getting-start-tdd/python/07-encapsulation-and-polymorphism.md +0 -217
  340. package/lib/assets/docs/article/getting-start-tdd/python/08-design-patterns.md +0 -181
  341. package/lib/assets/docs/article/getting-start-tdd/python/09-solid-principles-and-module-design.md +0 -139
  342. package/lib/assets/docs/article/getting-start-tdd/python/10-higher-order-functions-and-composition.md +0 -131
  343. package/lib/assets/docs/article/getting-start-tdd/python/11-immutable-data-and-pipeline.md +0 -125
  344. package/lib/assets/docs/article/getting-start-tdd/python/12-error-handling-and-type-safety.md +0 -135
  345. package/lib/assets/docs/article/getting-start-tdd/python/index.md +0 -55
  346. package/lib/assets/docs/article/getting-start-tdd/ruby/01-todo-list-and-first-test.md +0 -240
  347. package/lib/assets/docs/article/getting-start-tdd/ruby/02-fake-it-and-triangulation.md +0 -247
  348. package/lib/assets/docs/article/getting-start-tdd/ruby/03-obvious-implementation-and-refactoring.md +0 -237
  349. package/lib/assets/docs/article/getting-start-tdd/ruby/04-version-control-and-conventional-commits.md +0 -121
  350. package/lib/assets/docs/article/getting-start-tdd/ruby/05-package-management-and-static-analysis.md +0 -296
  351. package/lib/assets/docs/article/getting-start-tdd/ruby/06-task-runner-and-ci-cd.md +0 -257
  352. package/lib/assets/docs/article/getting-start-tdd/ruby/07-encapsulation-and-polymorphism.md +0 -288
  353. package/lib/assets/docs/article/getting-start-tdd/ruby/08-design-patterns.md +0 -338
  354. package/lib/assets/docs/article/getting-start-tdd/ruby/09-solid-principles-and-module-design.md +0 -205
  355. package/lib/assets/docs/article/getting-start-tdd/ruby/10-higher-order-functions-and-composition.md +0 -184
  356. package/lib/assets/docs/article/getting-start-tdd/ruby/11-immutable-data-and-pipeline.md +0 -246
  357. package/lib/assets/docs/article/getting-start-tdd/ruby/12-error-handling-and-type-safety.md +0 -407
  358. package/lib/assets/docs/article/getting-start-tdd/ruby/index.md +0 -83
  359. package/lib/assets/docs/article/getting-start-tdd/rust/01-todo-list-and-first-test.md +0 -220
  360. package/lib/assets/docs/article/getting-start-tdd/rust/02-fake-it-and-triangulation.md +0 -273
  361. package/lib/assets/docs/article/getting-start-tdd/rust/03-obvious-implementation-and-refactoring.md +0 -242
  362. package/lib/assets/docs/article/getting-start-tdd/rust/04-version-control-and-conventional-commits.md +0 -101
  363. package/lib/assets/docs/article/getting-start-tdd/rust/05-package-management-and-static-analysis.md +0 -221
  364. package/lib/assets/docs/article/getting-start-tdd/rust/06-task-runner-and-ci-cd.md +0 -173
  365. package/lib/assets/docs/article/getting-start-tdd/rust/07-encapsulation-and-polymorphism.md +0 -151
  366. package/lib/assets/docs/article/getting-start-tdd/rust/08-design-patterns.md +0 -154
  367. package/lib/assets/docs/article/getting-start-tdd/rust/09-solid-principles-and-module-design.md +0 -119
  368. package/lib/assets/docs/article/getting-start-tdd/rust/10-higher-order-functions-and-composition.md +0 -103
  369. package/lib/assets/docs/article/getting-start-tdd/rust/11-immutable-data-and-pipeline.md +0 -114
  370. package/lib/assets/docs/article/getting-start-tdd/rust/12-error-handling-and-type-safety.md +0 -121
  371. package/lib/assets/docs/article/getting-start-tdd/rust/index.md +0 -83
  372. package/lib/assets/docs/article/getting-start-tdd/scala/01-todo-list-and-first-test.md +0 -120
  373. package/lib/assets/docs/article/getting-start-tdd/scala/02-fake-it-and-triangulation.md +0 -116
  374. package/lib/assets/docs/article/getting-start-tdd/scala/03-obvious-implementation-and-refactoring.md +0 -108
  375. package/lib/assets/docs/article/getting-start-tdd/scala/04-version-control-and-conventional-commits.md +0 -132
  376. package/lib/assets/docs/article/getting-start-tdd/scala/05-package-management-and-static-analysis.md +0 -205
  377. package/lib/assets/docs/article/getting-start-tdd/scala/06-task-runner-and-ci-cd.md +0 -195
  378. package/lib/assets/docs/article/getting-start-tdd/scala/07-case-classes-and-traits.md +0 -148
  379. package/lib/assets/docs/article/getting-start-tdd/scala/08-pattern-matching-and-sealed-traits.md +0 -115
  380. package/lib/assets/docs/article/getting-start-tdd/scala/09-packages-and-module-design.md +0 -84
  381. package/lib/assets/docs/article/getting-start-tdd/scala/10-higher-order-functions-and-composition.md +0 -113
  382. package/lib/assets/docs/article/getting-start-tdd/scala/11-collections-and-lazy-evaluation.md +0 -103
  383. package/lib/assets/docs/article/getting-start-tdd/scala/12-error-handling-and-type-safety.md +0 -101
  384. package/lib/assets/docs/article/getting-start-tdd/scala/index.md +0 -65
  385. package/lib/assets/docs/article/grokking-concurrency/all/index.md +0 -403
  386. package/lib/assets/docs/article/grokking-concurrency/all/part-1-ch02-sequential.md +0 -563
  387. package/lib/assets/docs/article/grokking-concurrency/all/part-2-ch04-05-threads.md +0 -478
  388. package/lib/assets/docs/article/grokking-concurrency/all/part-3-ch06-multitasking.md +0 -529
  389. package/lib/assets/docs/article/grokking-concurrency/all/part-4-ch07-parallel-patterns.md +0 -429
  390. package/lib/assets/docs/article/grokking-concurrency/all/part-5-ch08-09-synchronization.md +0 -519
  391. package/lib/assets/docs/article/grokking-concurrency/all/part-6-ch10-11-nonblocking-io.md +0 -444
  392. package/lib/assets/docs/article/grokking-concurrency/all/part-7-ch12-async.md +0 -474
  393. package/lib/assets/docs/article/grokking-concurrency/all/part-8-ch13-mapreduce.md +0 -386
  394. package/lib/assets/docs/article/grokking-concurrency/clojure/index.md +0 -106
  395. package/lib/assets/docs/article/grokking-concurrency/clojure/part-1.md +0 -107
  396. package/lib/assets/docs/article/grokking-concurrency/clojure/part-2.md +0 -100
  397. package/lib/assets/docs/article/grokking-concurrency/clojure/part-3.md +0 -121
  398. package/lib/assets/docs/article/grokking-concurrency/clojure/part-4.md +0 -122
  399. package/lib/assets/docs/article/grokking-concurrency/clojure/part-5.md +0 -117
  400. package/lib/assets/docs/article/grokking-concurrency/clojure/part-6.md +0 -88
  401. package/lib/assets/docs/article/grokking-concurrency/clojure/part-7.md +0 -99
  402. package/lib/assets/docs/article/grokking-concurrency/clojure/part-8.md +0 -119
  403. package/lib/assets/docs/article/grokking-concurrency/csharp/index.md +0 -91
  404. package/lib/assets/docs/article/grokking-concurrency/csharp/part-1.md +0 -96
  405. package/lib/assets/docs/article/grokking-concurrency/csharp/part-2.md +0 -122
  406. package/lib/assets/docs/article/grokking-concurrency/csharp/part-3.md +0 -100
  407. package/lib/assets/docs/article/grokking-concurrency/csharp/part-4.md +0 -111
  408. package/lib/assets/docs/article/grokking-concurrency/csharp/part-5.md +0 -98
  409. package/lib/assets/docs/article/grokking-concurrency/csharp/part-6.md +0 -60
  410. package/lib/assets/docs/article/grokking-concurrency/csharp/part-7.md +0 -83
  411. package/lib/assets/docs/article/grokking-concurrency/csharp/part-8.md +0 -91
  412. package/lib/assets/docs/article/grokking-concurrency/fsharp/index.md +0 -55
  413. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-1.md +0 -79
  414. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-2.md +0 -102
  415. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-3.md +0 -93
  416. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-4.md +0 -109
  417. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-5.md +0 -103
  418. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-6.md +0 -92
  419. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-7.md +0 -120
  420. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-8.md +0 -106
  421. package/lib/assets/docs/article/grokking-concurrency/haskell/index.md +0 -238
  422. package/lib/assets/docs/article/grokking-concurrency/haskell/part-1.md +0 -95
  423. package/lib/assets/docs/article/grokking-concurrency/haskell/part-2.md +0 -95
  424. package/lib/assets/docs/article/grokking-concurrency/haskell/part-3.md +0 -90
  425. package/lib/assets/docs/article/grokking-concurrency/haskell/part-4.md +0 -105
  426. package/lib/assets/docs/article/grokking-concurrency/haskell/part-5.md +0 -98
  427. package/lib/assets/docs/article/grokking-concurrency/haskell/part-6.md +0 -94
  428. package/lib/assets/docs/article/grokking-concurrency/haskell/part-7.md +0 -110
  429. package/lib/assets/docs/article/grokking-concurrency/haskell/part-8.md +0 -117
  430. package/lib/assets/docs/article/grokking-concurrency/index.md +0 -66
  431. package/lib/assets/docs/article/grokking-concurrency/java/index.md +0 -92
  432. package/lib/assets/docs/article/grokking-concurrency/java/part-1.md +0 -307
  433. package/lib/assets/docs/article/grokking-concurrency/java/part-2.md +0 -333
  434. package/lib/assets/docs/article/grokking-concurrency/java/part-3.md +0 -220
  435. package/lib/assets/docs/article/grokking-concurrency/java/part-4.md +0 -212
  436. package/lib/assets/docs/article/grokking-concurrency/java/part-5.md +0 -111
  437. package/lib/assets/docs/article/grokking-concurrency/java/part-6.md +0 -68
  438. package/lib/assets/docs/article/grokking-concurrency/java/part-7.md +0 -100
  439. package/lib/assets/docs/article/grokking-concurrency/java/part-8.md +0 -100
  440. package/lib/assets/docs/article/grokking-concurrency/python/index.md +0 -303
  441. package/lib/assets/docs/article/grokking-concurrency/python/part-1.md +0 -238
  442. package/lib/assets/docs/article/grokking-concurrency/python/part-2.md +0 -417
  443. package/lib/assets/docs/article/grokking-concurrency/python/part-3.md +0 -226
  444. package/lib/assets/docs/article/grokking-concurrency/python/part-4.md +0 -298
  445. package/lib/assets/docs/article/grokking-concurrency/python/part-5.md +0 -314
  446. package/lib/assets/docs/article/grokking-concurrency/python/part-6.md +0 -296
  447. package/lib/assets/docs/article/grokking-concurrency/python/part-7.md +0 -313
  448. package/lib/assets/docs/article/grokking-concurrency/python/part-8.md +0 -359
  449. package/lib/assets/docs/article/grokking-concurrency/rust/index.md +0 -260
  450. package/lib/assets/docs/article/grokking-concurrency/rust/part-1.md +0 -107
  451. package/lib/assets/docs/article/grokking-concurrency/rust/part-2.md +0 -119
  452. package/lib/assets/docs/article/grokking-concurrency/rust/part-3.md +0 -125
  453. package/lib/assets/docs/article/grokking-concurrency/rust/part-4.md +0 -174
  454. package/lib/assets/docs/article/grokking-concurrency/rust/part-5.md +0 -157
  455. package/lib/assets/docs/article/grokking-concurrency/rust/part-6.md +0 -93
  456. package/lib/assets/docs/article/grokking-concurrency/rust/part-7.md +0 -132
  457. package/lib/assets/docs/article/grokking-concurrency/rust/part-8.md +0 -154
  458. package/lib/assets/docs/article/grokking-concurrency/scala/index.md +0 -59
  459. package/lib/assets/docs/article/grokking-concurrency/scala/part-1.md +0 -77
  460. package/lib/assets/docs/article/grokking-concurrency/scala/part-2.md +0 -111
  461. package/lib/assets/docs/article/grokking-concurrency/scala/part-3.md +0 -92
  462. package/lib/assets/docs/article/grokking-concurrency/scala/part-4.md +0 -109
  463. package/lib/assets/docs/article/grokking-concurrency/scala/part-5.md +0 -118
  464. package/lib/assets/docs/article/grokking-concurrency/scala/part-6.md +0 -82
  465. package/lib/assets/docs/article/grokking-concurrency/scala/part-7.md +0 -130
  466. package/lib/assets/docs/article/grokking-concurrency/scala/part-8.md +0 -128
  467. package/lib/assets/docs/article/grokkingfp/all/index.md +0 -368
  468. package/lib/assets/docs/article/grokkingfp/all/part-1-ch01-fp-introduction.md +0 -539
  469. package/lib/assets/docs/article/grokkingfp/all/part-1-ch02-pure-functions.md +0 -932
  470. package/lib/assets/docs/article/grokkingfp/all/part-2-ch03-immutable-data.md +0 -1137
  471. package/lib/assets/docs/article/grokkingfp/all/part-2-ch04-higher-order-functions.md +0 -1113
  472. package/lib/assets/docs/article/grokkingfp/all/part-2-ch05-flatmap.md +0 -1035
  473. package/lib/assets/docs/article/grokkingfp/all/part-3-ch06-option.md +0 -794
  474. package/lib/assets/docs/article/grokkingfp/all/part-3-ch07-either-adt.md +0 -880
  475. package/lib/assets/docs/article/grokkingfp/all/part-4-ch08-io-monad.md +0 -981
  476. package/lib/assets/docs/article/grokkingfp/all/part-4-ch09-streams.md +0 -935
  477. package/lib/assets/docs/article/grokkingfp/all/part-5-ch10-concurrency.md +0 -879
  478. package/lib/assets/docs/article/grokkingfp/all/part-6-ch11-application.md +0 -724
  479. package/lib/assets/docs/article/grokkingfp/all/part-6-ch12-testing.md +0 -635
  480. package/lib/assets/docs/article/grokkingfp/all/writing-plan.md +0 -721
  481. package/lib/assets/docs/article/grokkingfp/clojure/index.md +0 -276
  482. package/lib/assets/docs/article/grokkingfp/clojure/part-1.md +0 -676
  483. package/lib/assets/docs/article/grokkingfp/clojure/part-2.md +0 -652
  484. package/lib/assets/docs/article/grokkingfp/clojure/part-3.md +0 -629
  485. package/lib/assets/docs/article/grokkingfp/clojure/part-4.md +0 -706
  486. package/lib/assets/docs/article/grokkingfp/clojure/part-5.md +0 -760
  487. package/lib/assets/docs/article/grokkingfp/clojure/part-6.md +0 -730
  488. package/lib/assets/docs/article/grokkingfp/csharp/index.md +0 -246
  489. package/lib/assets/docs/article/grokkingfp/csharp/part-1.md +0 -820
  490. package/lib/assets/docs/article/grokkingfp/csharp/part-2.md +0 -980
  491. package/lib/assets/docs/article/grokkingfp/csharp/part-3.md +0 -990
  492. package/lib/assets/docs/article/grokkingfp/csharp/part-4.md +0 -958
  493. package/lib/assets/docs/article/grokkingfp/csharp/part-5.md +0 -956
  494. package/lib/assets/docs/article/grokkingfp/csharp/part-6.md +0 -748
  495. package/lib/assets/docs/article/grokkingfp/elixir/index.md +0 -203
  496. package/lib/assets/docs/article/grokkingfp/elixir/part-1.md +0 -721
  497. package/lib/assets/docs/article/grokkingfp/elixir/part-2.md +0 -847
  498. package/lib/assets/docs/article/grokkingfp/elixir/part-3.md +0 -994
  499. package/lib/assets/docs/article/grokkingfp/elixir/part-4.md +0 -983
  500. package/lib/assets/docs/article/grokkingfp/elixir/part-5.md +0 -1295
  501. package/lib/assets/docs/article/grokkingfp/elixir/part-6.md +0 -1058
  502. package/lib/assets/docs/article/grokkingfp/fsharp/index.md +0 -210
  503. package/lib/assets/docs/article/grokkingfp/fsharp/part-1.md +0 -723
  504. package/lib/assets/docs/article/grokkingfp/fsharp/part-2.md +0 -970
  505. package/lib/assets/docs/article/grokkingfp/fsharp/part-3.md +0 -981
  506. package/lib/assets/docs/article/grokkingfp/fsharp/part-4.md +0 -841
  507. package/lib/assets/docs/article/grokkingfp/fsharp/part-5.md +0 -920
  508. package/lib/assets/docs/article/grokkingfp/fsharp/part-6.md +0 -931
  509. package/lib/assets/docs/article/grokkingfp/haskell/index.md +0 -234
  510. package/lib/assets/docs/article/grokkingfp/haskell/part-1.md +0 -600
  511. package/lib/assets/docs/article/grokkingfp/haskell/part-2.md +0 -875
  512. package/lib/assets/docs/article/grokkingfp/haskell/part-3.md +0 -924
  513. package/lib/assets/docs/article/grokkingfp/haskell/part-4.md +0 -887
  514. package/lib/assets/docs/article/grokkingfp/haskell/part-5.md +0 -854
  515. package/lib/assets/docs/article/grokkingfp/haskell/part-6.md +0 -853
  516. package/lib/assets/docs/article/grokkingfp/index.md +0 -143
  517. package/lib/assets/docs/article/grokkingfp/java/index.md +0 -211
  518. package/lib/assets/docs/article/grokkingfp/java/part-1.md +0 -657
  519. package/lib/assets/docs/article/grokkingfp/java/part-2.md +0 -684
  520. package/lib/assets/docs/article/grokkingfp/java/part-3.md +0 -681
  521. package/lib/assets/docs/article/grokkingfp/java/part-4.md +0 -780
  522. package/lib/assets/docs/article/grokkingfp/java/part-5.md +0 -968
  523. package/lib/assets/docs/article/grokkingfp/java/part-6.md +0 -1337
  524. package/lib/assets/docs/article/grokkingfp/python/index.md +0 -258
  525. package/lib/assets/docs/article/grokkingfp/python/part-1.md +0 -452
  526. package/lib/assets/docs/article/grokkingfp/python/part-2.md +0 -967
  527. package/lib/assets/docs/article/grokkingfp/python/part-3.md +0 -1013
  528. package/lib/assets/docs/article/grokkingfp/python/part-4.md +0 -774
  529. package/lib/assets/docs/article/grokkingfp/python/part-5.md +0 -756
  530. package/lib/assets/docs/article/grokkingfp/python/part-6.md +0 -870
  531. package/lib/assets/docs/article/grokkingfp/ruby/index.md +0 -330
  532. package/lib/assets/docs/article/grokkingfp/ruby/part-1.md +0 -764
  533. package/lib/assets/docs/article/grokkingfp/ruby/part-2.md +0 -947
  534. package/lib/assets/docs/article/grokkingfp/ruby/part-3.md +0 -955
  535. package/lib/assets/docs/article/grokkingfp/ruby/part-4.md +0 -930
  536. package/lib/assets/docs/article/grokkingfp/ruby/part-5.md +0 -917
  537. package/lib/assets/docs/article/grokkingfp/ruby/part-6.md +0 -1421
  538. package/lib/assets/docs/article/grokkingfp/rust/index.md +0 -242
  539. package/lib/assets/docs/article/grokkingfp/rust/part-1.md +0 -643
  540. package/lib/assets/docs/article/grokkingfp/rust/part-2.md +0 -1069
  541. package/lib/assets/docs/article/grokkingfp/rust/part-3.md +0 -1003
  542. package/lib/assets/docs/article/grokkingfp/rust/part-4.md +0 -582
  543. package/lib/assets/docs/article/grokkingfp/rust/part-5.md +0 -714
  544. package/lib/assets/docs/article/grokkingfp/rust/part-6.md +0 -517
  545. package/lib/assets/docs/article/grokkingfp/scala/index.md +0 -171
  546. package/lib/assets/docs/article/grokkingfp/scala/part-1.md +0 -552
  547. package/lib/assets/docs/article/grokkingfp/scala/part-2.md +0 -955
  548. package/lib/assets/docs/article/grokkingfp/scala/part-3.md +0 -928
  549. package/lib/assets/docs/article/grokkingfp/scala/part-4.md +0 -751
  550. package/lib/assets/docs/article/grokkingfp/scala/part-5.md +0 -731
  551. package/lib/assets/docs/article/grokkingfp/scala/part-6.md +0 -876
  552. package/lib/assets/docs/article/grokkingfp/typescript/index.md +0 -273
  553. package/lib/assets/docs/article/grokkingfp/typescript/part-1.md +0 -570
  554. package/lib/assets/docs/article/grokkingfp/typescript/part-2.md +0 -1138
  555. package/lib/assets/docs/article/grokkingfp/typescript/part-3.md +0 -851
  556. package/lib/assets/docs/article/grokkingfp/typescript/part-4.md +0 -1096
  557. package/lib/assets/docs/article/grokkingfp/typescript/part-5.md +0 -726
  558. package/lib/assets/docs/article/grokkingfp/typescript/part-6.md +0 -991
  559. package/lib/assets/docs/article/practical-database-design/index.md +0 -121
  560. package/lib/assets/docs/article/practical-database-design/part1/chapter01.md +0 -297
  561. package/lib/assets/docs/article/practical-database-design/part1/chapter02.md +0 -527
  562. package/lib/assets/docs/article/practical-database-design/part1/chapter03.md +0 -566
  563. package/lib/assets/docs/article/practical-database-design/part2/chapter04.md +0 -933
  564. package/lib/assets/docs/article/practical-database-design/part2/chapter05.md +0 -1636
  565. package/lib/assets/docs/article/practical-database-design/part2/chapter06.md +0 -2725
  566. package/lib/assets/docs/article/practical-database-design/part2/chapter07.md +0 -2091
  567. package/lib/assets/docs/article/practical-database-design/part2/chapter08.md +0 -2114
  568. package/lib/assets/docs/article/practical-database-design/part2/chapter09.md +0 -2040
  569. package/lib/assets/docs/article/practical-database-design/part2/chapter10.md +0 -1396
  570. package/lib/assets/docs/article/practical-database-design/part2/chapter11.md +0 -1686
  571. package/lib/assets/docs/article/practical-database-design/part2/chapter12.md +0 -1426
  572. package/lib/assets/docs/article/practical-database-design/part2/chapter13.md +0 -1443
  573. package/lib/assets/docs/article/practical-database-design/part3/chapter14.md +0 -676
  574. package/lib/assets/docs/article/practical-database-design/part3/chapter15.md +0 -1634
  575. package/lib/assets/docs/article/practical-database-design/part3/chapter16.md +0 -1924
  576. package/lib/assets/docs/article/practical-database-design/part3/chapter17.md +0 -1717
  577. package/lib/assets/docs/article/practical-database-design/part3/chapter18.md +0 -2104
  578. package/lib/assets/docs/article/practical-database-design/part3/chapter19.md +0 -1132
  579. package/lib/assets/docs/article/practical-database-design/part3/chapter20.md +0 -1040
  580. package/lib/assets/docs/article/practical-database-design/part3/chapter21.md +0 -1391
  581. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter14-orm.md +0 -1000
  582. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter15-orm.md +0 -1309
  583. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter16-orm.md +0 -1175
  584. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter17-orm.md +0 -1593
  585. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter18-orm.md +0 -1192
  586. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter19-orm.md +0 -1025
  587. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter20-orm.md +0 -1762
  588. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter21-orm.md +0 -1456
  589. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter22-orm.md +0 -1887
  590. package/lib/assets/docs/article/practical-database-design/part4/chapter22.md +0 -974
  591. package/lib/assets/docs/article/practical-database-design/part4/chapter23.md +0 -2078
  592. package/lib/assets/docs/article/practical-database-design/part4/chapter24.md +0 -2448
  593. package/lib/assets/docs/article/practical-database-design/part4/chapter25.md +0 -3670
  594. package/lib/assets/docs/article/practical-database-design/part4/chapter26.md +0 -2925
  595. package/lib/assets/docs/article/practical-database-design/part4/chapter27.md +0 -3114
  596. package/lib/assets/docs/article/practical-database-design/part4/chapter28.md +0 -2706
  597. package/lib/assets/docs/article/practical-database-design/part4/chapter29.md +0 -2552
  598. package/lib/assets/docs/article/practical-database-design/part4/chapter30.md +0 -2188
  599. package/lib/assets/docs/article/practical-database-design/part4/chapter31.md +0 -1200
  600. package/lib/assets/docs/article/practical-database-design/part4/chapter32.md +0 -2109
  601. package/lib/assets/docs/article/practical-database-design/part5/chapter33.md +0 -1041
  602. package/lib/assets/docs/article/practical-database-design/part5/chapter34.md +0 -1618
  603. package/lib/assets/docs/article/practical-database-design/part5/chapter35.md +0 -1462
  604. package/lib/assets/docs/article/practical-database-design/part5/chapter36.md +0 -1301
  605. package/lib/assets/docs/article/practical-database-design/part5/chapter37.md +0 -1479
  606. package/lib/assets/docs/article/practical-database-design/part5/chapter38.md +0 -1707
  607. package/lib/assets/docs/article/practical-database-design/part5/chapter39.md +0 -2343
  608. package/lib/assets/docs/article/practical-database-design/study/study2-1.md +0 -1702
  609. package/lib/assets/docs/article/practical-database-design/study/study2-2.md +0 -1356
  610. package/lib/assets/docs/article/practical-database-design/study/study2-3.md +0 -2053
  611. package/lib/assets/docs/article/practical-database-design/study/study2-4.md +0 -2238
  612. package/lib/assets/docs/article/practical-database-design/study/study2-5.md +0 -2427
  613. package/lib/assets/docs/article/practical-database-design/study/study3-1.md +0 -2214
  614. package/lib/assets/docs/article/practical-database-design/study/study3-2.md +0 -2230
  615. package/lib/assets/docs/article/practical-database-design/study/study3-3.md +0 -2262
  616. package/lib/assets/docs/article/practical-database-design/study/study3-4.md +0 -2115
  617. package/lib/assets/docs/article/practical-database-design/study/study3-5.md +0 -2516
  618. package/lib/assets/docs/article/practical-database-design/study/study4-1.md +0 -2596
  619. package/lib/assets/docs/article/practical-database-design/study/study4-2.md +0 -2084
  620. package/lib/assets/docs/article/practical-database-design/study/study4-3.md +0 -1814
  621. package/lib/assets/docs/article/practical-database-design/study/study4-4.md +0 -1904
  622. package/lib/assets/docs/article/practical-database-design/study/study4-5.md +0 -2887
@@ -1,1443 +0,0 @@
1
- ---
2
- type: Article
3
- title: "第13章:API サービスの実装"
4
- description: "本章では、販売管理システムのデータベース設計を外部から利用できるようにするため、RESTful API サービスを実装します。"
5
- tags: [article, practical-database-design, part2]
6
- status: stable
7
- generated: { by: human:kakimomokuri, at: 2026-03-30T08:34:28Z }
8
- ---
9
-
10
- # 第13章:API サービスの実装
11
-
12
- 本章では、販売管理システムのデータベース設計を外部から利用できるようにするため、RESTful API サービスを実装します。ヘキサゴナルアーキテクチャ(Ports and Adapters)を採用し、ドメインロジックを外部技術から分離した保守性の高い API を構築します。
13
-
14
- ---
15
-
16
- ## 13.1 ヘキサゴナルアーキテクチャの復習
17
-
18
- ### Ports and Adapters パターンの概要
19
-
20
- ヘキサゴナルアーキテクチャ(Ports and Adapters パターン)は、Alistair Cockburn によって提唱された設計手法で、アプリケーションの中核となるビジネスロジック(ドメイン層)を外部の技術的詳細から完全に分離することを目的とします。
21
-
22
- ```plantuml
23
- @startuml hexagonal_architecture_sales
24
- !define RECTANGLE class
25
-
26
- package "Hexagonal Architecture (販売管理API)" {
27
-
28
- RECTANGLE "Application Core\n(Domain + Use Cases)" as core {
29
- - Product (商品)
30
- - Partner (取引先)
31
- - Order (受注)
32
- - Sales (売上)
33
- - Invoice (請求)
34
- - ProductUseCase
35
- - OrderUseCase
36
- - SalesUseCase
37
- }
38
-
39
- RECTANGLE "Input Adapters\n(Driving Side)" as input {
40
- - Spring Controllers
41
- - REST API Endpoints
42
- - Request Validation
43
- - Error Handling
44
- }
45
-
46
- RECTANGLE "Output Adapters\n(Driven Side)" as output {
47
- - MyBatis Repository
48
- - Database Access
49
- - Entity Mapping
50
- }
51
- }
52
-
53
- input --> core : "Input Ports\n(Use Cases)"
54
- core --> output : "Output Ports\n(Repository Interfaces)"
55
-
56
- note top of core
57
- 純粋なビジネスロジック
58
- MyBatis に依存しない
59
- 高速でテスト可能
60
- end note
61
-
62
- note left of input
63
- 外部からアプリケーションを
64
- 駆動するアダプター
65
- HTTP, REST等
66
- end note
67
-
68
- note right of output
69
- アプリケーションが外部の
70
- 技術を使うためのアダプター
71
- PostgreSQL, MyBatis等
72
- end note
73
- @enduml
74
- ```
75
-
76
- ### ドメイン中心設計
77
-
78
- ビジネスロジックを中心に据え、外部技術(DB、Web、UI など)を周辺に配置します。これにより、ビジネスルールが技術的な関心事から独立し、変更に強い設計が実現できます。
79
-
80
- ### 依存性の逆転
81
-
82
- ドメイン層は外部に依存せず、外部がドメイン層に依存します。具体的には、リポジトリのインターフェース(Output Port)をドメイン層で定義し、その実装(Adapter)をインフラストラクチャ層に配置します。
83
-
84
- ### テスト容易性
85
-
86
- モックやスタブを使った単体テストが容易になります。ドメインロジックを独立してテストでき、外部依存(データベースなど)なしで高速なテストが可能です。
87
-
88
- ---
89
-
90
- ## 13.2 アーキテクチャ構造
91
-
92
- ### レイヤー構成
93
-
94
- 販売管理 API の実装では、以下のレイヤー構造を採用します。
95
-
96
- ```
97
- src/main/java/com/example/sms/
98
- ├── domain/ # ドメイン層(純粋なビジネスロジック)
99
- │ ├── model/ # ドメインモデル(エンティティ、値オブジェクト)
100
- │ │ ├── department/ # 部門関連
101
- │ │ ├── employee/ # 社員関連
102
- │ │ ├── inventory/ # 在庫関連
103
- │ │ ├── partner/ # 取引先関連
104
- │ │ ├── product/ # 商品関連
105
- │ │ ├── purchase/ # 仕入関連
106
- │ │ └── sales/ # 販売関連
107
- │ ├── exception/ # ドメイン例外
108
- │ └── type/ # 値型定義
109
- │
110
- ├── application/ # アプリケーション層
111
- │ └── port/
112
- │ └── out/ # Output Port(リポジトリインターフェース)
113
- │
114
- ├── infrastructure/ # インフラストラクチャ層
115
- │ ├── in/ # Input Adapter(受信アダプター)
116
- │ │ ├── rest/ # REST API(Web実装)
117
- │ │ │ ├── controller/ # REST Controller(Spring MVC)
118
- │ │ │ ├── dto/ # Data Transfer Object
119
- │ │ │ └── exception/ # Exception Handler
120
- │ │ └── seed/ # Seed データ投入
121
- │ └── out/ # Output Adapter(送信アダプター)
122
- │ └── persistence/ # DB実装
123
- │ ├── mapper/ # MyBatis Mapper
124
- │ ├── repository/ # Repository実装
125
- │ └── typehandler/ # 型ハンドラー
126
- │
127
- └── config/ # 設定クラス
128
- ```
129
-
130
- ### Domain 層
131
-
132
- ビジネスルールとドメインモデルを定義します。外部技術に依存しない純粋な Java コードで構成されます。
133
-
134
- <details>
135
- <summary>Product.java(商品ドメインモデル)</summary>
136
-
137
- ```java
138
- package com.example.sms.domain.model.product;
139
-
140
- import lombok.AllArgsConstructor;
141
- import lombok.Builder;
142
- import lombok.Data;
143
- import lombok.NoArgsConstructor;
144
-
145
- import java.math.BigDecimal;
146
- import java.time.LocalDateTime;
147
-
148
- /**
149
- * 商品エンティティ.
150
- */
151
- @Data
152
- @Builder
153
- @NoArgsConstructor
154
- @AllArgsConstructor
155
- public class Product {
156
- private String productCode;
157
- private String productFullName;
158
- private String productName;
159
- private String productNameKana;
160
- private ProductCategory productCategory;
161
- private String modelNumber;
162
- @Builder.Default
163
- private BigDecimal sellingPrice = BigDecimal.ZERO;
164
- @Builder.Default
165
- private BigDecimal purchasePrice = BigDecimal.ZERO;
166
- private TaxCategory taxCategory;
167
- private String classificationCode;
168
- @Builder.Default
169
- private boolean isMiscellaneous = false;
170
- @Builder.Default
171
- private boolean isInventoryManaged = true;
172
- @Builder.Default
173
- private boolean isInventoryAllocated = true;
174
- private String supplierCode;
175
- private String supplierBranchNumber;
176
- private LocalDateTime createdAt;
177
- private String createdBy;
178
- private LocalDateTime updatedAt;
179
- private String updatedBy;
180
- }
181
- ```
182
-
183
- </details>
184
-
185
- ### Application 層
186
-
187
- ユースケースの実装とオーケストレーションを担当します。
188
-
189
- <details>
190
- <summary>ProductRepository.java(Output Port)</summary>
191
-
192
- ```java
193
- package com.example.sms.application.port.out;
194
-
195
- import com.example.sms.domain.model.product.Product;
196
- import com.example.sms.domain.model.product.ProductCategory;
197
-
198
- import java.util.List;
199
- import java.util.Optional;
200
-
201
- /**
202
- * 商品リポジトリ(Output Port).
203
- */
204
- public interface ProductRepository {
205
-
206
- void save(Product product);
207
-
208
- Optional<Product> findByCode(String productCode);
209
-
210
- List<Product> findAll();
211
-
212
- List<Product> findByCategory(ProductCategory category);
213
-
214
- List<Product> findByClassificationCode(String classificationCode);
215
-
216
- void update(Product product);
217
-
218
- void deleteByCode(String productCode);
219
-
220
- void deleteAll();
221
- }
222
- ```
223
-
224
- </details>
225
-
226
- ### Infrastructure 層
227
-
228
- 外部技術との接続を担当します。DB アクセス(MyBatis)や Web フレームワーク(Spring MVC)の実装を含みます。
229
-
230
- ### Input Port / Output Port の分離
231
-
232
- | ポート | 役割 | 例 |
233
- |--------|------|-----|
234
- | Input Port | アプリケーションへの入力インターフェース | `ProductUseCase`, `OrderUseCase` |
235
- | Output Port | アプリケーションからの出力インターフェース | `ProductRepository`, `OrderRepository` |
236
-
237
- ---
238
-
239
- ## 13.3 マスタ API の実装
240
-
241
- ### 商品マスタ API(CRUD エンドポイント)
242
-
243
- #### Output Port(リポジトリインターフェース)
244
-
245
- <details>
246
- <summary>ProductRepository.java</summary>
247
-
248
- ```java
249
- package com.example.sms.application.port.out;
250
-
251
- /**
252
- * 商品リポジトリ(Output Port)
253
- */
254
- public interface ProductRepository {
255
- Product save(Product product);
256
- List<Product> findAll();
257
- Optional<Product> findByCode(String productCode, LocalDate effectiveDate);
258
- List<Product> findByType(ProductType type);
259
- List<Product> findByCategoryCode(String categoryCode);
260
- void deleteByCode(String productCode, LocalDate effectiveDate);
261
- }
262
- ```
263
-
264
- </details>
265
-
266
- #### Input Port(ユースケースインターフェース)
267
-
268
- <details>
269
- <summary>コマンドオブジェクト</summary>
270
-
271
- ```java
272
- package com.example.sms.application.port.in;
273
-
274
- public record CreateProductCommand(
275
- String productCode,
276
- String productName,
277
- String categoryCode,
278
- ProductType productType,
279
- TaxType taxType,
280
- BigDecimal sellingPrice,
281
- BigDecimal purchasePrice
282
- ) {}
283
-
284
- public record UpdateProductCommand(
285
- String productCode,
286
- String productName,
287
- ProductType productType,
288
- TaxType taxType,
289
- BigDecimal sellingPrice,
290
- BigDecimal purchasePrice
291
- ) {}
292
- ```
293
-
294
- </details>
295
-
296
- ### TDD による実装(Red-Green-Refactor)
297
-
298
- #### Red: 失敗するテストを書く
299
-
300
- <details>
301
- <summary>ProductControllerTest.java</summary>
302
-
303
- ```java
304
- package com.example.sms.infrastructure.in.rest;
305
-
306
- import com.example.sms.application.port.out.ProductClassificationRepository;
307
- import com.example.sms.application.port.out.ProductRepository;
308
- import com.example.sms.domain.model.product.Product;
309
- import com.example.sms.domain.model.product.ProductCategory;
310
- import com.example.sms.domain.model.product.ProductClassification;
311
- import com.example.sms.domain.model.product.TaxCategory;
312
- import com.example.sms.testsetup.BaseIntegrationTest;
313
- import org.junit.jupiter.api.BeforeEach;
314
- import org.junit.jupiter.api.DisplayName;
315
- import org.junit.jupiter.api.Nested;
316
- import org.junit.jupiter.api.Test;
317
- import org.springframework.beans.factory.annotation.Autowired;
318
- import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
319
- import org.springframework.http.MediaType;
320
- import org.springframework.test.web.servlet.MockMvc;
321
-
322
- import java.math.BigDecimal;
323
-
324
- import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
325
- import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
326
-
327
- @AutoConfigureMockMvc
328
- @DisplayName("商品マスタ API テスト")
329
- class ProductControllerTest extends BaseIntegrationTest {
330
-
331
- private static final String TEST_CLASSIFICATION_CODE = "CAT-TEST";
332
-
333
- @Autowired
334
- private MockMvc mockMvc;
335
-
336
- @Autowired
337
- private ProductRepository productRepository;
338
-
339
- @Autowired
340
- private ProductClassificationRepository productClassificationRepository;
341
-
342
- @BeforeEach
343
- void setUp() {
344
- productRepository.deleteAll();
345
- productClassificationRepository.deleteAll();
346
- // テスト用の商品分類を作成
347
- ProductClassification classification = ProductClassification.builder()
348
- .classificationCode(TEST_CLASSIFICATION_CODE)
349
- .classificationName("テスト分類")
350
- .hierarchyLevel(1)
351
- .classificationPath("/CAT-TEST")
352
- .isLeaf(true)
353
- .build();
354
- productClassificationRepository.save(classification);
355
- }
356
-
357
- @Nested
358
- @DisplayName("POST /api/v1/products")
359
- class CreateProduct {
360
-
361
- @Test
362
- @DisplayName("商品を登録できる")
363
- void shouldCreateProduct() throws Exception {
364
- var request = """
365
- {
366
- "productCode": "NEW-001",
367
- "productFullName": "新規テスト商品 フルネーム",
368
- "productName": "新規テスト商品",
369
- "productNameKana": "シンキテストショウヒン",
370
- "productCategory": "PRODUCT",
371
- "modelNumber": "MODEL-001",
372
- "sellingPrice": 5000,
373
- "purchasePrice": 3000,
374
- "taxCategory": "EXCLUSIVE",
375
- "classificationCode": "CAT-TEST"
376
- }
377
- """;
378
-
379
- mockMvc.perform(post("/api/v1/products")
380
- .contentType(MediaType.APPLICATION_JSON)
381
- .content(request))
382
- .andExpect(status().isCreated())
383
- .andExpect(jsonPath("$.productCode").value("NEW-001"))
384
- .andExpect(jsonPath("$.productName").value("新規テスト商品"));
385
- }
386
- }
387
- }
388
- ```
389
-
390
- </details>
391
-
392
- #### Green: テストを通す実装
393
-
394
- <details>
395
- <summary>ProductController.java</summary>
396
-
397
- ```java
398
- package com.example.sms.infrastructure.in.rest.controller;
399
-
400
- import com.example.sms.application.port.out.ProductRepository;
401
- import com.example.sms.domain.exception.DuplicateProductException;
402
- import com.example.sms.domain.exception.ProductNotFoundException;
403
- import com.example.sms.domain.model.product.Product;
404
- import com.example.sms.infrastructure.in.rest.dto.CreateProductRequest;
405
- import com.example.sms.infrastructure.in.rest.dto.ProductResponse;
406
- import com.example.sms.infrastructure.in.rest.dto.UpdateProductRequest;
407
- import io.swagger.v3.oas.annotations.Operation;
408
- import io.swagger.v3.oas.annotations.Parameter;
409
- import io.swagger.v3.oas.annotations.responses.ApiResponse;
410
- import io.swagger.v3.oas.annotations.tags.Tag;
411
- import jakarta.validation.Valid;
412
- import org.springframework.http.HttpStatus;
413
- import org.springframework.http.ResponseEntity;
414
- import org.springframework.web.bind.annotation.*;
415
-
416
- import java.util.List;
417
-
418
- @RestController
419
- @RequestMapping("/api/v1/products")
420
- @Tag(name = "products", description = "商品マスタ API")
421
- public class ProductController {
422
-
423
- private final ProductRepository productRepository;
424
-
425
- public ProductController(ProductRepository productRepository) {
426
- this.productRepository = productRepository;
427
- }
428
-
429
- @GetMapping
430
- @Operation(summary = "商品一覧の取得")
431
- @ApiResponse(responseCode = "200", description = "商品一覧を返却")
432
- public ResponseEntity<List<ProductResponse>> getAllProducts() {
433
- List<Product> products = productRepository.findAll();
434
- List<ProductResponse> responses = products.stream()
435
- .map(ProductResponse::from)
436
- .toList();
437
- return ResponseEntity.ok(responses);
438
- }
439
-
440
- @GetMapping("/{productCode}")
441
- @Operation(summary = "商品の取得")
442
- @ApiResponse(responseCode = "200", description = "商品を返却")
443
- @ApiResponse(responseCode = "404", description = "商品が見つからない")
444
- public ResponseEntity<ProductResponse> getProduct(
445
- @Parameter(description = "商品コード")
446
- @PathVariable String productCode) {
447
-
448
- Product product = productRepository.findByCode(productCode)
449
- .orElseThrow(() -> new ProductNotFoundException(productCode));
450
-
451
- return ResponseEntity.ok(ProductResponse.from(product));
452
- }
453
-
454
- @PostMapping
455
- @Operation(summary = "商品の登録")
456
- @ApiResponse(responseCode = "201", description = "商品を登録")
457
- @ApiResponse(responseCode = "409", description = "商品が既に存在する")
458
- public ResponseEntity<ProductResponse> createProduct(
459
- @Valid @RequestBody CreateProductRequest request) {
460
-
461
- // 重複チェック
462
- productRepository.findByCode(request.productCode())
463
- .ifPresent(existing -> {
464
- throw new DuplicateProductException(request.productCode());
465
- });
466
-
467
- Product product = Product.builder()
468
- .productCode(request.productCode())
469
- .productFullName(request.productFullName())
470
- .productName(request.productName())
471
- .productNameKana(request.productNameKana())
472
- .productCategory(request.productCategory())
473
- .modelNumber(request.modelNumber())
474
- .sellingPrice(request.sellingPrice())
475
- .purchasePrice(request.purchasePrice())
476
- .taxCategory(request.taxCategory())
477
- .classificationCode(request.classificationCode())
478
- .build();
479
-
480
- productRepository.save(product);
481
-
482
- return ResponseEntity.status(HttpStatus.CREATED)
483
- .body(ProductResponse.from(product));
484
- }
485
- }
486
- ```
487
-
488
- </details>
489
-
490
- ### Controller・Service・Repository の実装
491
-
492
- #### Application Service
493
-
494
- <details>
495
- <summary>ProductService.java</summary>
496
-
497
- ```java
498
- @Service
499
- @Transactional
500
- public class ProductService implements ProductUseCase {
501
-
502
- private final ProductRepository productRepository;
503
-
504
- public ProductService(ProductRepository productRepository) {
505
- this.productRepository = productRepository;
506
- }
507
-
508
- @Override
509
- public Product createProduct(CreateProductCommand command) {
510
- LocalDate effectiveDate = LocalDate.now();
511
-
512
- // 重複チェック
513
- productRepository.findByCode(command.productCode(), effectiveDate)
514
- .ifPresent(existing -> {
515
- throw new DuplicateProductException(command.productCode());
516
- });
517
-
518
- Product product = new Product(
519
- command.productCode(),
520
- effectiveDate,
521
- command.productName(),
522
- command.categoryCode(),
523
- effectiveDate,
524
- command.productType(),
525
- command.taxType(),
526
- command.sellingPrice(),
527
- command.purchasePrice()
528
- );
529
-
530
- return productRepository.save(product);
531
- }
532
-
533
- @Override
534
- @Transactional(readOnly = true)
535
- public Product getProductByCode(String productCode) {
536
- LocalDate effectiveDate = LocalDate.now();
537
- return productRepository.findByCode(productCode, effectiveDate)
538
- .orElseThrow(() -> new ProductNotFoundException(productCode));
539
- }
540
- }
541
- ```
542
-
543
- </details>
544
-
545
- ### 顧客マスタ API(請求先・回収先の管理)
546
-
547
- 顧客マスタ API では、請求先と回収先の概念を正しく管理します。
548
-
549
- <details>
550
- <summary>CustomerController.java</summary>
551
-
552
- ```java
553
- @RestController
554
- @RequestMapping("/api/v1/customers")
555
- @Tag(name = "customers", description = "顧客 API")
556
- public class CustomerController {
557
-
558
- private final CustomerUseCase customerUseCase;
559
-
560
- @GetMapping("/{customerCode}")
561
- @Operation(summary = "顧客の取得")
562
- public ResponseEntity<CustomerResponse> getCustomer(
563
- @PathVariable String customerCode) {
564
-
565
- Customer customer = customerUseCase.getCustomerByCode(customerCode);
566
- return ResponseEntity.ok(CustomerResponse.from(customer));
567
- }
568
-
569
- @GetMapping("/{customerCode}/billing-destinations")
570
- @Operation(summary = "請求先一覧の取得")
571
- public ResponseEntity<List<BillingDestinationResponse>> getBillingDestinations(
572
- @PathVariable String customerCode) {
573
-
574
- List<BillingDestination> destinations =
575
- customerUseCase.getBillingDestinations(customerCode);
576
- return ResponseEntity.ok(destinations.stream()
577
- .map(BillingDestinationResponse::from)
578
- .toList());
579
- }
580
- }
581
- ```
582
-
583
- </details>
584
-
585
- ---
586
-
587
- ## 13.4 トランザクション API の実装
588
-
589
- ### 受注 API(受注登録・受注照会・受注明細照会)
590
-
591
- <details>
592
- <summary>OrderController.java</summary>
593
-
594
- ```java
595
- @RestController
596
- @RequestMapping("/api/v1/orders")
597
- @Tag(name = "orders", description = "受注 API")
598
- public class OrderController {
599
-
600
- private final OrderUseCase orderUseCase;
601
-
602
- @PostMapping
603
- @Operation(summary = "受注の登録")
604
- public ResponseEntity<OrderResponse> createOrder(
605
- @Valid @RequestBody CreateOrderRequest request) {
606
-
607
- Order order = orderUseCase.createOrder(request.toCommand());
608
- return ResponseEntity.status(HttpStatus.CREATED)
609
- .body(OrderResponse.from(order));
610
- }
611
-
612
- @GetMapping("/{orderNumber}")
613
- @Operation(summary = "受注の取得")
614
- public ResponseEntity<OrderResponse> getOrder(
615
- @PathVariable String orderNumber) {
616
-
617
- Order order = orderUseCase.getOrderByNumber(orderNumber);
618
- return ResponseEntity.ok(OrderResponse.from(order));
619
- }
620
-
621
- @GetMapping("/{orderNumber}/details")
622
- @Operation(summary = "受注明細の取得")
623
- public ResponseEntity<List<OrderDetailResponse>> getOrderDetails(
624
- @PathVariable String orderNumber) {
625
-
626
- List<OrderDetail> details = orderUseCase.getOrderDetails(orderNumber);
627
- return ResponseEntity.ok(details.stream()
628
- .map(OrderDetailResponse::from)
629
- .toList());
630
- }
631
- }
632
- ```
633
-
634
- </details>
635
-
636
- ### 出荷 API(出荷指示・出荷確定)
637
-
638
- <details>
639
- <summary>ShipmentController.java</summary>
640
-
641
- ```java
642
- @RestController
643
- @RequestMapping("/api/v1/shipments")
644
- @Tag(name = "shipments", description = "出荷 API")
645
- public class ShipmentController {
646
-
647
- private final ShipmentUseCase shipmentUseCase;
648
-
649
- @PostMapping("/instructions")
650
- @Operation(summary = "出荷指示の登録")
651
- public ResponseEntity<ShipmentInstructionResponse> createShipmentInstruction(
652
- @Valid @RequestBody CreateShipmentInstructionRequest request) {
653
-
654
- ShipmentInstruction instruction =
655
- shipmentUseCase.createInstruction(request.toCommand());
656
- return ResponseEntity.status(HttpStatus.CREATED)
657
- .body(ShipmentInstructionResponse.from(instruction));
658
- }
659
-
660
- @PostMapping("/{shipmentNumber}/confirm")
661
- @Operation(summary = "出荷確定")
662
- public ResponseEntity<ShipmentResponse> confirmShipment(
663
- @PathVariable String shipmentNumber) {
664
-
665
- Shipment shipment = shipmentUseCase.confirmShipment(shipmentNumber);
666
- return ResponseEntity.ok(ShipmentResponse.from(shipment));
667
- }
668
- }
669
- ```
670
-
671
- </details>
672
-
673
- ### 売上 API(売上計上・売上照会)
674
-
675
- <details>
676
- <summary>SalesController.java</summary>
677
-
678
- ```java
679
- @RestController
680
- @RequestMapping("/api/v1/sales")
681
- @Tag(name = "sales", description = "売上 API")
682
- public class SalesController {
683
-
684
- private final SalesUseCase salesUseCase;
685
-
686
- @PostMapping
687
- @Operation(summary = "売上計上")
688
- public ResponseEntity<SalesResponse> recordSales(
689
- @Valid @RequestBody RecordSalesRequest request) {
690
-
691
- Sales sales = salesUseCase.recordSales(request.toCommand());
692
- return ResponseEntity.status(HttpStatus.CREATED)
693
- .body(SalesResponse.from(sales));
694
- }
695
-
696
- @GetMapping
697
- @Operation(summary = "売上一覧の取得")
698
- public ResponseEntity<List<SalesResponse>> getSalesList(
699
- @RequestParam(required = false) LocalDate fromDate,
700
- @RequestParam(required = false) LocalDate toDate,
701
- @RequestParam(required = false) String customerCode) {
702
-
703
- List<Sales> salesList = salesUseCase.getSalesList(fromDate, toDate, customerCode);
704
- return ResponseEntity.ok(salesList.stream()
705
- .map(SalesResponse::from)
706
- .toList());
707
- }
708
- }
709
- ```
710
-
711
- </details>
712
-
713
- ### 請求・入金 API(請求締め・入金消込)
714
-
715
- <details>
716
- <summary>InvoiceController.java / ReceiptController.java</summary>
717
-
718
- ```java
719
- @RestController
720
- @RequestMapping("/api/v1/invoices")
721
- @Tag(name = "invoices", description = "請求 API")
722
- public class InvoiceController {
723
-
724
- private final InvoiceUseCase invoiceUseCase;
725
-
726
- @PostMapping("/closing")
727
- @Operation(summary = "請求締め処理")
728
- public ResponseEntity<List<InvoiceResponse>> executeClosing(
729
- @Valid @RequestBody InvoiceClosingRequest request) {
730
-
731
- List<Invoice> invoices = invoiceUseCase.executeClosing(
732
- request.closingDate(),
733
- request.customerCode()
734
- );
735
- return ResponseEntity.ok(invoices.stream()
736
- .map(InvoiceResponse::from)
737
- .toList());
738
- }
739
- }
740
-
741
- @RestController
742
- @RequestMapping("/api/v1/receipts")
743
- @Tag(name = "receipts", description = "入金 API")
744
- public class ReceiptController {
745
-
746
- private final ReceiptUseCase receiptUseCase;
747
-
748
- @PostMapping
749
- @Operation(summary = "入金登録")
750
- public ResponseEntity<ReceiptResponse> recordReceipt(
751
- @Valid @RequestBody RecordReceiptRequest request) {
752
-
753
- Receipt receipt = receiptUseCase.recordReceipt(request.toCommand());
754
- return ResponseEntity.status(HttpStatus.CREATED)
755
- .body(ReceiptResponse.from(receipt));
756
- }
757
-
758
- @PostMapping("/{receiptNumber}/apply")
759
- @Operation(summary = "入金消込")
760
- public ResponseEntity<ReceiptResponse> applyReceipt(
761
- @PathVariable String receiptNumber,
762
- @Valid @RequestBody ApplyReceiptRequest request) {
763
-
764
- Receipt receipt = receiptUseCase.applyToInvoices(
765
- receiptNumber,
766
- request.invoiceNumbers()
767
- );
768
- return ResponseEntity.ok(ReceiptResponse.from(receipt));
769
- }
770
- }
771
- ```
772
-
773
- </details>
774
-
775
- ---
776
-
777
- ## 13.5 エラーハンドリング
778
-
779
- ### グローバル例外ハンドラー(@RestControllerAdvice)
780
-
781
- <details>
782
- <summary>GlobalExceptionHandler.java</summary>
783
-
784
- ```java
785
- package com.example.sms.infrastructure.in.rest.exception;
786
-
787
- import com.example.sms.domain.exception.BusinessRuleViolationException;
788
- import com.example.sms.domain.exception.DuplicateResourceException;
789
- import com.example.sms.domain.exception.OptimisticLockException;
790
- import com.example.sms.domain.exception.ResourceNotFoundException;
791
- import com.example.sms.infrastructure.in.rest.dto.ErrorResponse;
792
- import com.example.sms.infrastructure.in.rest.dto.ValidationErrorResponse;
793
- import org.slf4j.Logger;
794
- import org.slf4j.LoggerFactory;
795
- import org.springframework.http.HttpStatus;
796
- import org.springframework.http.ResponseEntity;
797
- import org.springframework.validation.FieldError;
798
- import org.springframework.web.bind.MethodArgumentNotValidException;
799
- import org.springframework.web.bind.annotation.ExceptionHandler;
800
- import org.springframework.web.bind.annotation.RestControllerAdvice;
801
-
802
- import java.time.Instant;
803
- import java.util.Map;
804
- import java.util.concurrent.ConcurrentHashMap;
805
-
806
- @RestControllerAdvice
807
- public class GlobalExceptionHandler {
808
-
809
- private static final Logger LOG = LoggerFactory.getLogger(GlobalExceptionHandler.class);
810
-
811
- @ExceptionHandler(ResourceNotFoundException.class)
812
- public ResponseEntity<ErrorResponse> handleResourceNotFound(ResourceNotFoundException e) {
813
- LOG.warn("Resource not found: {}", e.getMessage());
814
-
815
- ErrorResponse response = new ErrorResponse(
816
- HttpStatus.NOT_FOUND.value(),
817
- "NOT_FOUND",
818
- e.getMessage(),
819
- Instant.now()
820
- );
821
-
822
- return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response);
823
- }
824
-
825
- @ExceptionHandler(DuplicateResourceException.class)
826
- public ResponseEntity<ErrorResponse> handleDuplicateResource(DuplicateResourceException e) {
827
- LOG.warn("Duplicate resource: {}", e.getMessage());
828
-
829
- ErrorResponse response = new ErrorResponse(
830
- HttpStatus.CONFLICT.value(),
831
- "CONFLICT",
832
- e.getMessage(),
833
- Instant.now()
834
- );
835
-
836
- return ResponseEntity.status(HttpStatus.CONFLICT).body(response);
837
- }
838
-
839
- @ExceptionHandler(BusinessRuleViolationException.class)
840
- public ResponseEntity<ErrorResponse> handleBusinessRuleViolation(BusinessRuleViolationException e) {
841
- LOG.warn("Business rule violation: {}", e.getMessage());
842
-
843
- ErrorResponse response = new ErrorResponse(
844
- HttpStatus.UNPROCESSABLE_ENTITY.value(),
845
- "BUSINESS_RULE_VIOLATION",
846
- e.getMessage(),
847
- Instant.now()
848
- );
849
-
850
- return ResponseEntity.status(HttpStatus.UNPROCESSABLE_ENTITY).body(response);
851
- }
852
-
853
- @ExceptionHandler(OptimisticLockException.class)
854
- public ResponseEntity<ErrorResponse> handleOptimisticLock(OptimisticLockException e) {
855
- LOG.warn("Optimistic lock exception: {}", e.getMessage());
856
-
857
- ErrorResponse response = new ErrorResponse(
858
- HttpStatus.CONFLICT.value(),
859
- "OPTIMISTIC_LOCK",
860
- e.getMessage(),
861
- Instant.now()
862
- );
863
-
864
- return ResponseEntity.status(HttpStatus.CONFLICT).body(response);
865
- }
866
-
867
- @ExceptionHandler(MethodArgumentNotValidException.class)
868
- public ResponseEntity<ValidationErrorResponse> handleValidationErrors(
869
- MethodArgumentNotValidException e) {
870
-
871
- Map<String, String> errors = new ConcurrentHashMap<>();
872
- e.getBindingResult().getAllErrors().forEach(error -> {
873
- String fieldName = ((FieldError) error).getField();
874
- String errorMessage = error.getDefaultMessage();
875
- errors.put(fieldName, errorMessage);
876
- });
877
-
878
- ValidationErrorResponse response = new ValidationErrorResponse(
879
- HttpStatus.BAD_REQUEST.value(),
880
- "VALIDATION_ERROR",
881
- "入力値が不正です",
882
- errors,
883
- Instant.now()
884
- );
885
-
886
- return ResponseEntity.badRequest().body(response);
887
- }
888
-
889
- @ExceptionHandler(Exception.class)
890
- public ResponseEntity<ErrorResponse> handleGenericException(Exception e) {
891
- LOG.error("Unexpected error occurred", e);
892
-
893
- ErrorResponse response = new ErrorResponse(
894
- HttpStatus.INTERNAL_SERVER_ERROR.value(),
895
- "INTERNAL_ERROR",
896
- "システムエラーが発生しました",
897
- Instant.now()
898
- );
899
-
900
- return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
901
- }
902
- }
903
- ```
904
-
905
- </details>
906
-
907
- ### ドメイン例外の定義と変換
908
-
909
- <details>
910
- <summary>ドメイン例外クラス</summary>
911
-
912
- ```java
913
- package com.example.sms.domain.exception;
914
-
915
- public abstract class ResourceNotFoundException extends RuntimeException {
916
- protected ResourceNotFoundException(String message) {
917
- super(message);
918
- }
919
- }
920
-
921
- public class ProductNotFoundException extends ResourceNotFoundException {
922
- public ProductNotFoundException(String productCode) {
923
- super("商品が見つかりません: " + productCode);
924
- }
925
- }
926
-
927
- public class OrderNotFoundException extends ResourceNotFoundException {
928
- public OrderNotFoundException(String orderNumber) {
929
- super("受注が見つかりません: " + orderNumber);
930
- }
931
- }
932
-
933
- public abstract class BusinessRuleViolationException extends RuntimeException {
934
- protected BusinessRuleViolationException(String message) {
935
- super(message);
936
- }
937
- }
938
-
939
- public class CreditLimitExceededException extends BusinessRuleViolationException {
940
- public CreditLimitExceededException(String customerCode, BigDecimal limit, BigDecimal amount) {
941
- super(String.format(
942
- "与信限度額を超過しています(顧客: %s, 限度額: %s, 申請額: %s)",
943
- customerCode, limit, amount
944
- ));
945
- }
946
- }
947
-
948
- public class InsufficientInventoryException extends BusinessRuleViolationException {
949
- public InsufficientInventoryException(String productCode, BigDecimal available, BigDecimal requested) {
950
- super(String.format(
951
- "在庫が不足しています(商品: %s, 有効在庫: %s, 要求数量: %s)",
952
- productCode, available, requested
953
- ));
954
- }
955
- }
956
- ```
957
-
958
- </details>
959
-
960
- ### ProblemDetail によるエラーレスポンス
961
-
962
- RFC 7807 に準拠した ProblemDetail 形式でエラーを返却することも可能です。
963
-
964
- <details>
965
- <summary>エラーレスポンス DTO</summary>
966
-
967
- ```java
968
- public record ErrorResponse(
969
- int status,
970
- String code,
971
- String message,
972
- Instant timestamp
973
- ) {}
974
-
975
- public record ValidationErrorResponse(
976
- int status,
977
- String code,
978
- String message,
979
- Map<String, String> errors,
980
- Instant timestamp
981
- ) {}
982
- ```
983
-
984
- </details>
985
-
986
- ### バリデーションエラーの処理
987
-
988
- リクエスト DTO に Bean Validation アノテーションを付与し、バリデーションエラーを統一的に処理します。
989
-
990
- <details>
991
- <summary>CreateProductRequest.java</summary>
992
-
993
- ```java
994
- public record CreateProductRequest(
995
- @NotBlank(message = "商品コードは必須です")
996
- String productCode,
997
-
998
- @NotBlank(message = "商品名は必須です")
999
- String productName,
1000
-
1001
- @NotBlank(message = "商品分類コードは必須です")
1002
- String categoryCode,
1003
-
1004
- @NotNull(message = "商品区分は必須です")
1005
- ProductType productType,
1006
-
1007
- @NotNull(message = "税区分は必須です")
1008
- TaxType taxType,
1009
-
1010
- @NotNull(message = "販売単価は必須です")
1011
- @Positive(message = "販売単価は正の数である必要があります")
1012
- BigDecimal sellingPrice,
1013
-
1014
- @NotNull(message = "仕入単価は必須です")
1015
- @Positive(message = "仕入単価は正の数である必要があります")
1016
- BigDecimal purchasePrice
1017
- ) {}
1018
- ```
1019
-
1020
- </details>
1021
-
1022
- ---
1023
-
1024
- ## 13.6 API ドキュメント
1025
-
1026
- ### OpenAPI / Swagger の設定
1027
-
1028
- <details>
1029
- <summary>OpenApiConfig.java</summary>
1030
-
1031
- ```java
1032
- @Configuration
1033
- public class OpenApiConfig {
1034
-
1035
- @Bean
1036
- public OpenAPI openAPI() {
1037
- return new OpenAPI()
1038
- .info(new Info()
1039
- .title("販売管理システム API")
1040
- .description("TDD で育てる販売管理システムの API ドキュメント")
1041
- .version("1.0.0"))
1042
- .servers(List.of(
1043
- new Server()
1044
- .url("http://localhost:8080")
1045
- .description("開発サーバー")))
1046
- .tags(List.of(
1047
- new Tag().name("products").description("商品マスタ API"),
1048
- new Tag().name("partners").description("取引先マスタ API"),
1049
- new Tag().name("customers").description("顧客 API"),
1050
- new Tag().name("orders").description("受注 API"),
1051
- new Tag().name("shipments").description("出荷 API"),
1052
- new Tag().name("sales").description("売上 API"),
1053
- new Tag().name("invoices").description("請求 API"),
1054
- new Tag().name("receipts").description("入金 API")));
1055
- }
1056
- }
1057
- ```
1058
-
1059
- </details>
1060
-
1061
- ### エンドポイントの文書化
1062
-
1063
- <details>
1064
- <summary>アノテーションによるドキュメント化</summary>
1065
-
1066
- ```java
1067
- @GetMapping("/{productCode}")
1068
- @Operation(
1069
- summary = "商品の取得",
1070
- description = "商品コードを指定して商品情報を取得します"
1071
- )
1072
- @ApiResponse(responseCode = "200", description = "商品を返却")
1073
- @ApiResponse(responseCode = "404", description = "商品が見つからない")
1074
- public ResponseEntity<ProductResponse> getProduct(
1075
- @Parameter(description = "商品コード", example = "BEEF-001")
1076
- @PathVariable String productCode) {
1077
- // ...
1078
- }
1079
- ```
1080
-
1081
- </details>
1082
-
1083
- ### リクエスト・レスポンスのスキーマ定義
1084
-
1085
- application.yml に Swagger UI の設定を追加します。
1086
-
1087
- <details>
1088
- <summary>application.yml(Swagger 設定)</summary>
1089
-
1090
- ```yaml
1091
- springdoc:
1092
- swagger-ui:
1093
- path: /swagger-ui.html
1094
- tags-sorter: alpha
1095
- operations-sorter: alpha
1096
- api-docs:
1097
- path: /api-docs
1098
- ```
1099
-
1100
- </details>
1101
-
1102
- Swagger UI にアクセスすると、API ドキュメントが自動生成されます。
1103
-
1104
- ---
1105
-
1106
- ## 13.7 API インテグレーションテスト
1107
-
1108
- ### テストコンテナによる統合テスト環境
1109
-
1110
- Testcontainers を使用して、実際のデータベース環境で API の統合テストを実行します。
1111
-
1112
- <details>
1113
- <summary>テスト基盤クラス</summary>
1114
-
1115
- ```java
1116
- @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
1117
- @Testcontainers
1118
- @ActiveProfiles("test")
1119
- public abstract class IntegrationTestBase {
1120
-
1121
- @Container
1122
- static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16")
1123
- .withDatabaseName("sales_test")
1124
- .withUsername("test")
1125
- .withPassword("test");
1126
-
1127
- @DynamicPropertySource
1128
- static void configureProperties(DynamicPropertyRegistry registry) {
1129
- registry.add("spring.datasource.url", postgres::getJdbcUrl);
1130
- registry.add("spring.datasource.username", postgres::getUsername);
1131
- registry.add("spring.datasource.password", postgres::getPassword);
1132
- }
1133
-
1134
- @Autowired
1135
- protected TestRestTemplate restTemplate;
1136
-
1137
- @Autowired
1138
- protected ObjectMapper objectMapper;
1139
- }
1140
- ```
1141
-
1142
- </details>
1143
-
1144
- ### REST API エンドポイントのテスト
1145
-
1146
- <details>
1147
- <summary>ProductApiIntegrationTest.java</summary>
1148
-
1149
- ```java
1150
- @DisplayName("商品 API 統合テスト")
1151
- class ProductApiIntegrationTest extends IntegrationTestBase {
1152
-
1153
- @Nested
1154
- @DisplayName("商品登録・取得フロー")
1155
- class ProductCrudFlow {
1156
-
1157
- @Test
1158
- @DisplayName("商品を登録して取得できる")
1159
- void shouldCreateAndRetrieveProduct() {
1160
- // Given: 商品登録リクエスト
1161
- var createRequest = new CreateProductRequest(
1162
- "BEEF-INT-001",
1163
- "統合テスト用商品",
1164
- "CAT-BEEF",
1165
- ProductType.PRODUCT,
1166
- TaxType.STANDARD,
1167
- new BigDecimal("5000"),
1168
- new BigDecimal("3000")
1169
- );
1170
-
1171
- // When: 商品を登録
1172
- ResponseEntity<ProductResponse> createResponse = restTemplate.postForEntity(
1173
- "/api/v1/products",
1174
- createRequest,
1175
- ProductResponse.class
1176
- );
1177
-
1178
- // Then: 登録成功
1179
- assertThat(createResponse.getStatusCode()).isEqualTo(HttpStatus.CREATED);
1180
- assertThat(createResponse.getBody()).isNotNull();
1181
- assertThat(createResponse.getBody().productCode()).isEqualTo("BEEF-INT-001");
1182
-
1183
- // When: 登録した商品を取得
1184
- ResponseEntity<ProductResponse> getResponse = restTemplate.getForEntity(
1185
- "/api/v1/products/BEEF-INT-001",
1186
- ProductResponse.class
1187
- );
1188
-
1189
- // Then: 取得成功
1190
- assertThat(getResponse.getStatusCode()).isEqualTo(HttpStatus.OK);
1191
- assertThat(getResponse.getBody().productName()).isEqualTo("統合テスト用商品");
1192
- }
1193
-
1194
- @Test
1195
- @DisplayName("存在しない商品を取得すると404エラー")
1196
- void shouldReturn404WhenProductNotFound() {
1197
- // When
1198
- ResponseEntity<ErrorResponse> response = restTemplate.getForEntity(
1199
- "/api/v1/products/NOT-EXIST",
1200
- ErrorResponse.class
1201
- );
1202
-
1203
- // Then
1204
- assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
1205
- assertThat(response.getBody().code()).isEqualTo("NOT_FOUND");
1206
- }
1207
- }
1208
- }
1209
- ```
1210
-
1211
- </details>
1212
-
1213
- ### データベース状態の検証
1214
-
1215
- <details>
1216
- <summary>OrderApiIntegrationTest.java</summary>
1217
-
1218
- ```java
1219
- @DisplayName("受注 API 統合テスト")
1220
- class OrderApiIntegrationTest extends IntegrationTestBase {
1221
-
1222
- @Autowired
1223
- private JdbcTemplate jdbcTemplate;
1224
-
1225
- @BeforeEach
1226
- void setUp() {
1227
- // テストデータのセットアップ
1228
- jdbcTemplate.execute("""
1229
- INSERT INTO 商品 (商品コード, 適用開始日, 商品名, 販売単価, 仕入単価)
1230
- VALUES ('BEEF-001', CURRENT_DATE, 'テスト牛肉', 5000, 3000)
1231
- ON CONFLICT DO NOTHING
1232
- """);
1233
- }
1234
-
1235
- @Test
1236
- @DisplayName("受注登録時にデータベースに正しく保存される")
1237
- void shouldPersistOrderToDatabase() {
1238
- // Given
1239
- var createRequest = new CreateOrderRequest(
1240
- "CUS-001",
1241
- LocalDate.now(),
1242
- List.of(new OrderDetailRequest("BEEF-001", 10, new BigDecimal("5000")))
1243
- );
1244
-
1245
- // When
1246
- ResponseEntity<OrderResponse> response = restTemplate.postForEntity(
1247
- "/api/v1/orders",
1248
- createRequest,
1249
- OrderResponse.class
1250
- );
1251
-
1252
- // Then: API レスポンスの検証
1253
- assertThat(response.getStatusCode()).isEqualTo(HttpStatus.CREATED);
1254
- String orderNumber = response.getBody().orderNumber();
1255
-
1256
- // Then: データベース状態の検証
1257
- Integer count = jdbcTemplate.queryForObject(
1258
- "SELECT COUNT(*) FROM 受注 WHERE 受注番号 = ?",
1259
- Integer.class,
1260
- orderNumber
1261
- );
1262
- assertThat(count).isEqualTo(1);
1263
-
1264
- // Then: 明細データの検証
1265
- Integer detailCount = jdbcTemplate.queryForObject(
1266
- "SELECT COUNT(*) FROM 受注明細 WHERE 受注番号 = ?",
1267
- Integer.class,
1268
- orderNumber
1269
- );
1270
- assertThat(detailCount).isEqualTo(1);
1271
- }
1272
- }
1273
- ```
1274
-
1275
- </details>
1276
-
1277
- ### テストデータのセットアップとクリーンアップ
1278
-
1279
- <details>
1280
- <summary>TestDataManager.java</summary>
1281
-
1282
- ```java
1283
- @Component
1284
- @Profile("test")
1285
- public class TestDataManager {
1286
-
1287
- private final JdbcTemplate jdbcTemplate;
1288
-
1289
- public TestDataManager(JdbcTemplate jdbcTemplate) {
1290
- this.jdbcTemplate = jdbcTemplate;
1291
- }
1292
-
1293
- @Transactional
1294
- public void setupMasterData() {
1295
- // 商品分類マスタ
1296
- jdbcTemplate.execute("""
1297
- INSERT INTO 商品分類 (商品分類コード, 適用開始日, 商品分類名)
1298
- VALUES ('CAT-BEEF', CURRENT_DATE, '牛肉'),
1299
- ('CAT-PORK', CURRENT_DATE, '豚肉')
1300
- ON CONFLICT DO NOTHING
1301
- """);
1302
-
1303
- // 取引先マスタ
1304
- jdbcTemplate.execute("""
1305
- INSERT INTO 取引先 (取引先コード, 取引先名, 取引先区分コード)
1306
- VALUES ('CUS-001', 'テスト顧客', '1'),
1307
- ('SUP-001', 'テスト仕入先', '2')
1308
- ON CONFLICT DO NOTHING
1309
- """);
1310
- }
1311
-
1312
- @Transactional
1313
- public void cleanupTransactionData() {
1314
- // トランザクションデータのクリーンアップ(外部キー制約の順序に注意)
1315
- jdbcTemplate.execute("DELETE FROM 入金明細");
1316
- jdbcTemplate.execute("DELETE FROM 入金");
1317
- jdbcTemplate.execute("DELETE FROM 請求明細");
1318
- jdbcTemplate.execute("DELETE FROM 請求");
1319
- jdbcTemplate.execute("DELETE FROM 売上明細");
1320
- jdbcTemplate.execute("DELETE FROM 売上");
1321
- jdbcTemplate.execute("DELETE FROM 出荷明細");
1322
- jdbcTemplate.execute("DELETE FROM 出荷");
1323
- jdbcTemplate.execute("DELETE FROM 受注明細");
1324
- jdbcTemplate.execute("DELETE FROM 受注");
1325
- }
1326
-
1327
- @Transactional
1328
- public void cleanupAll() {
1329
- cleanupTransactionData();
1330
- jdbcTemplate.execute("DELETE FROM 商品");
1331
- jdbcTemplate.execute("DELETE FROM 商品分類");
1332
- jdbcTemplate.execute("DELETE FROM 取引先");
1333
- }
1334
- }
1335
- ```
1336
-
1337
- </details>
1338
-
1339
- <details>
1340
- <summary>統合テストでの使用例</summary>
1341
-
1342
- ```java
1343
- @DisplayName("販売フロー統合テスト")
1344
- class SalesFlowIntegrationTest extends IntegrationTestBase {
1345
-
1346
- @Autowired
1347
- private TestDataManager testDataManager;
1348
-
1349
- @BeforeEach
1350
- void setUp() {
1351
- testDataManager.cleanupTransactionData();
1352
- testDataManager.setupMasterData();
1353
- }
1354
-
1355
- @AfterEach
1356
- void tearDown() {
1357
- testDataManager.cleanupTransactionData();
1358
- }
1359
-
1360
- @Test
1361
- @DisplayName("受注から売上までの一連のフローが正常に動作する")
1362
- void shouldCompleteSalesFlow() {
1363
- // 1. 受注登録
1364
- var orderRequest = createOrderRequest();
1365
- ResponseEntity<OrderResponse> orderResponse = restTemplate.postForEntity(
1366
- "/api/v1/orders",
1367
- orderRequest,
1368
- OrderResponse.class
1369
- );
1370
- assertThat(orderResponse.getStatusCode()).isEqualTo(HttpStatus.CREATED);
1371
- String orderNumber = orderResponse.getBody().orderNumber();
1372
-
1373
- // 2. 出荷指示
1374
- var shipmentRequest = new CreateShipmentInstructionRequest(orderNumber);
1375
- ResponseEntity<ShipmentInstructionResponse> shipmentResponse = restTemplate.postForEntity(
1376
- "/api/v1/shipments/instructions",
1377
- shipmentRequest,
1378
- ShipmentInstructionResponse.class
1379
- );
1380
- assertThat(shipmentResponse.getStatusCode()).isEqualTo(HttpStatus.CREATED);
1381
- String shipmentNumber = shipmentResponse.getBody().shipmentNumber();
1382
-
1383
- // 3. 出荷確定
1384
- ResponseEntity<ShipmentResponse> confirmResponse = restTemplate.postForEntity(
1385
- "/api/v1/shipments/" + shipmentNumber + "/confirm",
1386
- null,
1387
- ShipmentResponse.class
1388
- );
1389
- assertThat(confirmResponse.getStatusCode()).isEqualTo(HttpStatus.OK);
1390
-
1391
- // 4. 売上計上の確認
1392
- ResponseEntity<SalesResponse[]> salesResponse = restTemplate.getForEntity(
1393
- "/api/v1/sales?orderNumber=" + orderNumber,
1394
- SalesResponse[].class
1395
- );
1396
- assertThat(salesResponse.getStatusCode()).isEqualTo(HttpStatus.OK);
1397
- assertThat(salesResponse.getBody()).hasSize(1);
1398
- }
1399
- }
1400
- ```
1401
-
1402
- </details>
1403
-
1404
- ---
1405
-
1406
- ## 本章のまとめ
1407
-
1408
- 本章では、販売管理システムの API サービスを実装しました。
1409
-
1410
- ### 実装したコンポーネント
1411
-
1412
- | カテゴリ | 内容 |
1413
- |----------|------|
1414
- | アーキテクチャ | ヘキサゴナルアーキテクチャ(Ports & Adapters) |
1415
- | マスタ API | 商品マスタ、顧客マスタ |
1416
- | トランザクション API | 受注、出荷、売上、請求・入金 |
1417
- | エラーハンドリング | グローバル例外ハンドラー、ドメイン例外 |
1418
- | API ドキュメント | OpenAPI / Swagger UI |
1419
-
1420
- ### アーキテクチャの利点
1421
-
1422
- 1. **テスト容易性**: ドメインロジックを独立してテスト可能
1423
- 2. **技術変更の容易さ**: アダプターを差し替えるだけで技術を変更可能
1424
- 3. **保守性**: 関心事の分離による高い保守性
1425
- 4. **チーム開発**: レイヤーごとに並行開発が可能
1426
-
1427
- ### エンドポイント一覧
1428
-
1429
- | メソッド | パス | 説明 |
1430
- |----------|------|------|
1431
- | GET | `/api/v1/products` | 商品一覧の取得 |
1432
- | GET | `/api/v1/products/{code}` | 商品の取得 |
1433
- | POST | `/api/v1/products` | 商品の登録 |
1434
- | PUT | `/api/v1/products/{code}` | 商品の更新 |
1435
- | DELETE | `/api/v1/products/{code}` | 商品の削除 |
1436
- | GET | `/api/v1/orders` | 受注一覧の取得 |
1437
- | POST | `/api/v1/orders` | 受注の登録 |
1438
- | POST | `/api/v1/shipments/instructions` | 出荷指示の登録 |
1439
- | POST | `/api/v1/shipments/{number}/confirm` | 出荷確定 |
1440
- | POST | `/api/v1/sales` | 売上計上 |
1441
- | POST | `/api/v1/invoices/closing` | 請求締め処理 |
1442
- | POST | `/api/v1/receipts` | 入金登録 |
1443
- | POST | `/api/v1/receipts/{number}/apply` | 入金消込 |