@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,1702 +0,0 @@
1
- ---
2
- type: Article
3
- title: "実践データベース設計:販売管理システム 研究 1 - モノリスサービスの実装"
4
- description: "本研究では、API サーバー構成(第13章)とは異なるアプローチとして、モノリスアーキテクチャによる販売管理システムを実装します。"
5
- tags: [article, practical-database-design, study]
6
- status: stable
7
- generated: { by: human:kakimomokuri, at: 2026-03-30T08:34:28Z }
8
- ---
9
-
10
- # 実践データベース設計:販売管理システム 研究 1 - モノリスサービスの実装
11
-
12
- ## はじめに
13
-
14
- 本研究では、API サーバー構成(第13章)とは異なるアプローチとして、**モノリスアーキテクチャ**による販売管理システムを実装します。UI(テンプレートエンジン)、ビジネスロジック、データベースアクセスがすべて同一サーバー内で動作する、伝統的かつ堅実なアーキテクチャです。
15
-
16
- ---
17
-
18
- ## 第14章:モノリスアーキテクチャの基礎
19
-
20
- ### 14.1 モノリスアーキテクチャとは
21
-
22
- **モノリス(Monolith / Monolithic Architecture)**とは、「API」と「UI」と「ビジネスロジック」が **1つの実行ユニット**(1つのプロジェクトやバイナリ)にまとまっているアーキテクチャを指します。
23
-
24
- ```plantuml
25
- @startuml monolith_architecture
26
- !define RECTANGLE class
27
-
28
- title モノリスアーキテクチャ
29
-
30
- package "モノリスサーバー (単一の実行ユニット)" {
31
-
32
- package "プレゼンテーション層" {
33
- [Controller]
34
- [Thymeleaf テンプレート]
35
- [静的リソース (CSS/JS)]
36
- }
37
-
38
- package "ビジネスロジック層" {
39
- [Application Service]
40
- [Domain Model]
41
- [Domain Service]
42
- }
43
-
44
- package "データアクセス層" {
45
- [Repository]
46
- [MyBatis Mapper]
47
- }
48
- }
49
-
50
- database "PostgreSQL" as DB
51
-
52
- actor "ブラウザ" as Browser
53
-
54
- Browser --> [Controller] : HTTP リクエスト
55
- [Controller] --> [Thymeleaf テンプレート] : モデル渡し
56
- [Thymeleaf テンプレート] --> Browser : HTML レスポンス
57
- [Controller] --> [Application Service]
58
- [Application Service] --> [Domain Model]
59
- [Application Service] --> [Repository]
60
- [Repository] --> [MyBatis Mapper]
61
- [MyBatis Mapper] --> DB
62
-
63
- note right of "モノリスサーバー (単一の実行ユニット)"
64
- すべてが1つのプロセスで動作
65
- - 同一JVMで実行
66
- - 同一デプロイユニット
67
- - 密結合だが開発・運用がシンプル
68
- end note
69
-
70
- @enduml
71
- ```
72
-
73
- **モノリスの主な特徴:**
74
-
75
- | 特徴 | 説明 |
76
- |------|------|
77
- | **単一デプロイ** | アプリケーション全体が1つのアーティファクト(JAR/WAR)としてデプロイ |
78
- | **同一プロセス** | UI、ビジネスロジック、データアクセスが同じ JVM で動作 |
79
- | **テンプレートエンジン** | サーバーサイドで HTML を生成してブラウザに返却 |
80
- | **セッション管理** | サーバーサイドでユーザーセッションを管理 |
81
- | **トランザクション境界** | 単一プロセス内でのローカルトランザクション |
82
-
83
- ---
84
-
85
- ### 14.2 API サーバー vs モノリス
86
-
87
- ```plantuml
88
- @startuml api_vs_monolith
89
- !define RECTANGLE class
90
-
91
- left to right direction
92
-
93
- package "API サーバー構成(疎結合)" {
94
- package "フロントエンド" as FE {
95
- [React/Vue/Angular]
96
- [SPA]
97
- }
98
-
99
- package "バックエンド" as BE {
100
- [REST API]
101
- [JSON レスポンス]
102
- }
103
-
104
- database "DB1" as DB_API
105
-
106
- FE --> BE : HTTP/JSON
107
- BE --> DB_API
108
- }
109
-
110
- package "モノリス構成(密結合)" {
111
- package "モノリスサーバー" as Mono {
112
- [Controller + View]
113
- [テンプレートエンジン]
114
- [ビジネスロジック]
115
- [データアクセス]
116
- }
117
-
118
- database "DB2" as DB_Mono
119
-
120
- Mono --> DB_Mono
121
- }
122
-
123
- actor "ブラウザ (API)" as Browser1
124
- actor "ブラウザ (Mono)" as Browser2
125
-
126
- Browser1 --> FE
127
- Browser2 --> Mono : HTTP (HTML)
128
-
129
- @enduml
130
- ```
131
-
132
- | 観点 | API サーバー構成 | モノリス構成 |
133
- |------|-----------------|-------------|
134
- | **結合度** | 疎結合(フロントエンドとバックエンドが分離) | 密結合(すべてが同一サーバー) |
135
- | **フロントエンド** | SPA(React/Vue/Angular) | テンプレートエンジン(Thymeleaf) |
136
- | **通信方式** | REST API(JSON) | サーバーサイドレンダリング(HTML) |
137
- | **開発チーム** | フロント/バックエンドで分業可能 | フルスタックで開発 |
138
- | **デプロイ** | 別々にデプロイ可能 | 単一アーティファクトをデプロイ |
139
- | **スケーリング** | 個別にスケール可能 | 全体をスケール |
140
- | **複雑さ** | API 設計・認証・CORS などが必要 | シンプル、設定が少ない |
141
- | **初期開発速度** | 環境構築に時間がかかる | 素早く開発開始できる |
142
- | **SEO** | SSR/SSG が必要な場合あり | サーバーサイドレンダリングで SEO フレンドリー |
143
-
144
- ### 14.3 モノリスを選択すべき場面
145
-
146
- **モノリスが適している状況:**
147
-
148
- 1. **小〜中規模のチーム**:専門のフロントエンドチームがいない場合
149
- 2. **社内システム**:SEO 不要、限られたユーザー数
150
- 3. **業務アプリケーション**:複雑な業務フローをサーバーサイドで処理
151
- 4. **迅速な開発**:MVP やプロトタイプの素早い構築
152
- 5. **運用コスト重視**:インフラ構成をシンプルに保ちたい場合
153
-
154
- ---
155
-
156
- ### 14.4 モノリスにおけるヘキサゴナルアーキテクチャ
157
-
158
- モノリスであっても、ヘキサゴナルアーキテクチャ(Ports and Adapters)を採用することで、保守性の高い設計を実現できます。
159
-
160
- ```plantuml
161
- @startuml hexagonal_monolith
162
- !define RECTANGLE class
163
-
164
- package "Hexagonal Architecture (モノリス版)" {
165
-
166
- RECTANGLE "Application Core\n(Domain + Use Cases)" as core {
167
- - Product (商品)
168
- - Partner (取引先)
169
- - SalesOrder (受注)
170
- - Shipment (出荷)
171
- - Invoice (請求)
172
- - ProductUseCase
173
- - SalesOrderUseCase
174
- - InvoiceUseCase
175
- }
176
-
177
- RECTANGLE "Input Adapters\n(Driving Side)" as input {
178
- - Thymeleaf Controller
179
- - フォーム処理
180
- - セッション管理
181
- - リクエスト検証
182
- }
183
-
184
- RECTANGLE "Output Adapters\n(Driven Side)" as output {
185
- - MyBatis Repository
186
- - Database Access
187
- - Entity Mapping
188
- - 帳票出力
189
- }
190
- }
191
-
192
- input --> core : "Input Ports\n(Use Cases)"
193
- core --> output : "Output Ports\n(Repository Interfaces)"
194
-
195
- note top of core
196
- 純粋なビジネスロジック
197
- UI 技術(Thymeleaf)に依存しない
198
- テスト可能な状態を維持
199
- end note
200
-
201
- note left of input
202
- HTML フォームからの入力
203
- サーバーサイドレンダリング
204
- セッションベースの認証
205
- end note
206
-
207
- note right of output
208
- PostgreSQL + MyBatis
209
- 帳票生成(PDF/Excel)
210
- ファイル出力
211
- end note
212
-
213
- @enduml
214
- ```
215
-
216
- ---
217
-
218
- ### 14.5 ディレクトリ構成
219
-
220
- ```
221
- apps/sms/backend/src/main/java/com/example/sms/
222
- ├── domain/ # ドメイン層(API版と共通)
223
- │ ├── model/
224
- │ │ ├── product/
225
- │ │ ├── partner/
226
- │ │ ├── sales/
227
- │ │ ├── shipping/
228
- │ │ ├── invoice/
229
- │ │ └── receipt/
230
- │ └── exception/
231
- │
232
- ├── application/ # アプリケーション層(API版と共通)
233
- │ ├── port/
234
- │ │ ├── in/ # Input Port(ユースケース)
235
- │ │ └── out/ # Output Port(リポジトリ)
236
- │ └── service/
237
- │
238
- ├── infrastructure/
239
- │ ├── out/
240
- │ │ └── persistence/ # Output Adapter(DB実装)
241
- │ │ ├── mapper/
242
- │ │ ├── repository/
243
- │ │ └── typehandler/
244
- │ └── in/
245
- │ └── web/ # Input Adapter(Web実装)
246
- │ ├── controller/ # Thymeleaf Controller
247
- │ ├── form/ # フォームオブジェクト
248
- │ └── helper/ # ビューヘルパー
249
- │
250
- └── config/
251
-
252
- apps/sms/backend/src/main/resources/
253
- ├── templates/ # Thymeleaf テンプレート
254
- │ ├── layout/ # 共通レイアウト
255
- │ ├── products/ # 商品マスタ画面
256
- │ ├── partners/ # 取引先マスタ画面
257
- │ ├── orders/ # 受注画面
258
- │ ├── shipments/ # 出荷画面
259
- │ └── invoices/ # 請求画面
260
- ├── static/ # 静的リソース
261
- │ ├── css/
262
- │ ├── js/
263
- │ └── images/
264
- ├── mapper/ # MyBatis Mapper XML
265
- └── messages.properties # メッセージリソース
266
- ```
267
-
268
- ---
269
-
270
- ### 14.6 技術スタックの導入
271
-
272
- #### build.gradle.kts
273
-
274
- <details>
275
- <summary>コード例: build.gradle.kts</summary>
276
-
277
- ```kotlin
278
- plugins {
279
- id("java")
280
- id("org.springframework.boot") version "3.4.1"
281
- id("io.spring.dependency-management") version "1.1.7"
282
- }
283
-
284
- java {
285
- toolchain {
286
- languageVersion = JavaLanguageVersion.of(21)
287
- }
288
- }
289
-
290
- dependencies {
291
- // Spring Boot Web(REST APIではなくMVCとして使用)
292
- implementation("org.springframework.boot:spring-boot-starter-web")
293
- implementation("org.springframework.boot:spring-boot-starter-validation")
294
-
295
- // Thymeleaf(テンプレートエンジン)
296
- implementation("org.springframework.boot:spring-boot-starter-thymeleaf")
297
- implementation("nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:3.4.0")
298
-
299
- // htmx(モダンなインタラクティブUI)
300
- // CDN から読み込むため依存関係は不要
301
-
302
- // MyBatis
303
- implementation("org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.4")
304
-
305
- // PostgreSQL
306
- runtimeOnly("org.postgresql:postgresql")
307
-
308
- // Flyway
309
- implementation("org.flywaydb:flyway-core")
310
- implementation("org.flywaydb:flyway-database-postgresql")
311
-
312
- // Webjars(Bootstrap等のフロントエンドライブラリ)
313
- implementation("org.webjars:bootstrap:5.3.3")
314
- implementation("org.webjars:webjars-locator-core:0.59")
315
-
316
- // 帳票出力
317
- implementation("org.apache.poi:poi-ooxml:5.3.0") // Excel
318
- implementation("io.github.openhtmltopdf:openhtmltopdf-pdfbox:1.1.22") // PDF(Thymeleaf + HTML/CSS)
319
- implementation("io.github.openhtmltopdf:openhtmltopdf-slf4j:1.1.22")
320
-
321
- // Test
322
- testImplementation("org.springframework.boot:spring-boot-starter-test")
323
- testImplementation("org.testcontainers:postgresql:1.20.4")
324
- testImplementation("org.testcontainers:junit-jupiter:1.20.4")
325
-
326
- // Lombok
327
- compileOnly("org.projectlombok:lombok")
328
- annotationProcessor("org.projectlombok:lombok")
329
- }
330
- ```
331
-
332
- </details>
333
-
334
- ---
335
-
336
- ### 14.7 共通レイアウト(layout/default.html)
337
-
338
- <details>
339
- <summary>コード例: layout/default.html</summary>
340
-
341
- ```html
342
- <!DOCTYPE html>
343
- <html xmlns:th="http://www.thymeleaf.org"
344
- xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
345
- lang="ja">
346
- <head>
347
- <meta charset="UTF-8">
348
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
349
- <title layout:title-pattern="$CONTENT_TITLE - $LAYOUT_TITLE">販売管理システム</title>
350
-
351
- <!-- Bootstrap CSS -->
352
- <link rel="stylesheet" th:href="@{/webjars/bootstrap/css/bootstrap.min.css}">
353
-
354
- <!-- カスタム CSS -->
355
- <link rel="stylesheet" th:href="@{/css/style.css}">
356
-
357
- <!-- htmx(部分更新用) -->
358
- <script src="https://unpkg.com/htmx.org@1.9.12"></script>
359
- </head>
360
- <body>
361
- <!-- ナビゲーションバー -->
362
- <nav class="navbar navbar-expand-lg navbar-dark bg-primary">
363
- <div class="container-fluid">
364
- <a class="navbar-brand" th:href="@{/}">販売管理システム</a>
365
- <div class="collapse navbar-collapse" id="navbarNav">
366
- <ul class="navbar-nav">
367
- <li class="nav-item dropdown">
368
- <a class="nav-link dropdown-toggle" href="#" data-bs-toggle="dropdown">マスタ</a>
369
- <ul class="dropdown-menu">
370
- <li><a class="dropdown-item" th:href="@{/products}">商品マスタ</a></li>
371
- <li><a class="dropdown-item" th:href="@{/partners}">取引先マスタ</a></li>
372
- </ul>
373
- </li>
374
- <li class="nav-item dropdown">
375
- <a class="nav-link dropdown-toggle" href="#" data-bs-toggle="dropdown">販売</a>
376
- <ul class="dropdown-menu">
377
- <li><a class="dropdown-item" th:href="@{/orders}">受注</a></li>
378
- <li><a class="dropdown-item" th:href="@{/shipments}">出荷</a></li>
379
- </ul>
380
- </li>
381
- <li class="nav-item dropdown">
382
- <a class="nav-link dropdown-toggle" href="#" data-bs-toggle="dropdown">債権</a>
383
- <ul class="dropdown-menu">
384
- <li><a class="dropdown-item" th:href="@{/invoices}">請求</a></li>
385
- <li><a class="dropdown-item" th:href="@{/receipts}">入金</a></li>
386
- </ul>
387
- </li>
388
- </ul>
389
- </div>
390
- </div>
391
- </nav>
392
-
393
- <!-- フラッシュメッセージ -->
394
- <div class="container mt-3">
395
- <div th:if="${successMessage}" class="alert alert-success alert-dismissible fade show">
396
- <span th:text="${successMessage}"></span>
397
- <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
398
- </div>
399
- <div th:if="${errorMessage}" class="alert alert-danger alert-dismissible fade show">
400
- <span th:text="${errorMessage}"></span>
401
- <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
402
- </div>
403
- </div>
404
-
405
- <!-- メインコンテンツ -->
406
- <main class="container mt-4">
407
- <div layout:fragment="content"></div>
408
- </main>
409
-
410
- <!-- Bootstrap JS -->
411
- <script th:src="@{/webjars/bootstrap/js/bootstrap.bundle.min.js}"></script>
412
- <th:block layout:fragment="scripts"></th:block>
413
- </body>
414
- </html>
415
- ```
416
-
417
- </details>
418
-
419
- ---
420
-
421
- ### 14.8 htmx による部分更新
422
-
423
- モノリスアプリケーションでも、**htmx** を使用することで SPA のような操作性を実現できます。
424
-
425
- ```plantuml
426
- @startuml htmx_flow
427
- title htmx による部分更新
428
-
429
- actor "ブラウザ" as Browser
430
- participant "htmx" as htmx
431
- participant "Controller" as Controller
432
- participant "Thymeleaf\nフラグメント" as Fragment
433
-
434
- Browser -> htmx : ボタンクリック
435
- htmx -> Controller : AJAX リクエスト\n(hx-get/hx-post)
436
- Controller -> Fragment : フラグメントを\nレンダリング
437
- Fragment --> Controller : HTML フラグメント
438
- Controller --> htmx : HTML レスポンス
439
- htmx -> Browser : DOM を部分更新\n(hx-target)
440
-
441
- note right of htmx
442
- htmx は特別な属性(hx-*)を
443
- 使って AJAX 通信を行う
444
- JavaScript を書かずに
445
- インタラクティブな UI を実現
446
- end note
447
-
448
- @enduml
449
- ```
450
-
451
- #### htmx の基本的な使い方
452
-
453
- <details>
454
- <summary>コード例: htmx による商品検索</summary>
455
-
456
- ```html
457
- <!-- 商品検索(部分更新) -->
458
- <input type="text" class="form-control"
459
- hx-get="/products/search"
460
- hx-trigger="keyup changed delay:300ms"
461
- hx-target="#search-results"
462
- hx-indicator="#loading">
463
- <span id="loading" class="htmx-indicator">検索中...</span>
464
-
465
- <div id="search-results">
466
- <!-- Controller から返される HTML フラグメント -->
467
- </div>
468
- ```
469
-
470
- </details>
471
-
472
- ---
473
-
474
- ## 第15章:マスタの実装
475
-
476
- ### 15.1 商品マスタ画面の設計
477
-
478
- | 画面 | URL | メソッド | 説明 |
479
- |------|-----|---------|------|
480
- | 商品一覧 | /products | GET | 商品の検索・一覧表示 |
481
- | 商品詳細 | /products/{productCode} | GET | 商品の詳細表示 |
482
- | 商品登録 | /products/new | GET | 登録フォーム表示 |
483
- | 商品登録処理 | /products | POST | 登録処理 |
484
- | 商品編集 | /products/{productCode}/edit | GET | 編集フォーム表示 |
485
- | 商品更新処理 | /products/{productCode} | POST | 更新処理(PUT 代替) |
486
- | 商品削除処理 | /products/{productCode}/delete | POST | 削除処理(DELETE 代替) |
487
-
488
- ### 15.2 フォームオブジェクトの設計
489
-
490
- <details>
491
- <summary>コード例: ProductForm.java</summary>
492
-
493
- ```java
494
- package com.example.sms.infrastructure.in.web.form;
495
-
496
- import com.example.sms.application.port.in.CreateProductCommand;
497
- import com.example.sms.domain.model.product.ProductCategory;
498
- import jakarta.validation.constraints.NotBlank;
499
- import jakarta.validation.constraints.NotNull;
500
- import jakarta.validation.constraints.Positive;
501
- import jakarta.validation.constraints.Size;
502
- import lombok.Data;
503
-
504
- import java.math.BigDecimal;
505
-
506
- /**
507
- * 商品登録・編集フォーム
508
- */
509
- @Data
510
- public class ProductForm {
511
-
512
- @NotBlank(message = "商品コードは必須です")
513
- @Size(max = 20, message = "商品コードは20文字以内で入力してください")
514
- private String productCode;
515
-
516
- @NotBlank(message = "商品名は必須です")
517
- @Size(max = 100, message = "商品名は100文字以内で入力してください")
518
- private String productName;
519
-
520
- @NotNull(message = "商品区分は必須です")
521
- private ProductCategory category;
522
-
523
- @Positive(message = "単価は正の数で入力してください")
524
- private BigDecimal unitPrice;
525
-
526
- /**
527
- * フォームをコマンドに変換
528
- */
529
- public CreateProductCommand toCommand() {
530
- return CreateProductCommand.builder()
531
- .productCode(this.productCode)
532
- .productName(this.productName)
533
- .category(this.category)
534
- .unitPrice(this.unitPrice)
535
- .build();
536
- }
537
-
538
- /**
539
- * エンティティからフォームを生成
540
- */
541
- public static ProductForm from(Product product) {
542
- ProductForm form = new ProductForm();
543
- form.setProductCode(product.getProductCode());
544
- form.setProductName(product.getProductName());
545
- form.setCategory(product.getCategory());
546
- form.setUnitPrice(product.getUnitPrice());
547
- return form;
548
- }
549
- }
550
- ```
551
-
552
- </details>
553
-
554
- ### 15.3 商品 Controller の TDD 実装
555
-
556
- <details>
557
- <summary>コード例: ProductControllerTest.java</summary>
558
-
559
- ```java
560
- @SpringBootTest
561
- @AutoConfigureMockMvc
562
- @Testcontainers
563
- @DisplayName("商品マスタ画面")
564
- class ProductControllerTest {
565
-
566
- @Container
567
- static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16")
568
- .withDatabaseName("sms_test");
569
-
570
- @DynamicPropertySource
571
- static void configureProperties(DynamicPropertyRegistry registry) {
572
- registry.add("spring.datasource.url", postgres::getJdbcUrl);
573
- }
574
-
575
- @Autowired
576
- private MockMvc mockMvc;
577
-
578
- @Test
579
- @DisplayName("商品一覧画面を表示できる")
580
- void shouldDisplayProductList() throws Exception {
581
- mockMvc.perform(get("/products"))
582
- .andExpect(status().isOk())
583
- .andExpect(view().name("products/list"))
584
- .andExpect(model().attributeExists("products"));
585
- }
586
-
587
- @Test
588
- @DisplayName("商品を登録できる")
589
- void shouldCreateProduct() throws Exception {
590
- mockMvc.perform(post("/products")
591
- .param("productCode", "NEW-001")
592
- .param("productName", "新規商品")
593
- .param("category", "FINISHED_GOODS")
594
- .param("unitPrice", "1000"))
595
- .andExpect(status().is3xxRedirection())
596
- .andExpect(redirectedUrl("/products"))
597
- .andExpect(flash().attribute("successMessage", containsString("登録")));
598
- }
599
- }
600
- ```
601
-
602
- </details>
603
-
604
- ### 15.4 商品 Controller
605
-
606
- <details>
607
- <summary>コード例: ProductController.java</summary>
608
-
609
- ```java
610
- @Controller
611
- @RequestMapping("/products")
612
- public class ProductController {
613
-
614
- private final ProductUseCase productUseCase;
615
-
616
- public ProductController(ProductUseCase productUseCase) {
617
- this.productUseCase = productUseCase;
618
- }
619
-
620
- @GetMapping
621
- public String list(@RequestParam(required = false) ProductCategory category, Model model) {
622
- List<Product> products = category != null
623
- ? productUseCase.findByCategory(category)
624
- : productUseCase.findAll();
625
- model.addAttribute("products", products);
626
- model.addAttribute("categories", ProductCategory.values());
627
- return "products/list";
628
- }
629
-
630
- @GetMapping("/new")
631
- public String newForm(Model model) {
632
- model.addAttribute("form", new ProductForm());
633
- model.addAttribute("categories", ProductCategory.values());
634
- return "products/new";
635
- }
636
-
637
- @PostMapping
638
- public String create(
639
- @Valid @ModelAttribute("form") ProductForm form,
640
- BindingResult bindingResult,
641
- RedirectAttributes redirectAttributes,
642
- Model model) {
643
-
644
- if (bindingResult.hasErrors()) {
645
- model.addAttribute("categories", ProductCategory.values());
646
- return "products/new";
647
- }
648
-
649
- productUseCase.createProduct(form.toCommand());
650
- redirectAttributes.addFlashAttribute("successMessage", "商品を登録しました");
651
- return "redirect:/products";
652
- }
653
-
654
- @GetMapping("/{productCode}/edit")
655
- public String editForm(@PathVariable String productCode, Model model) {
656
- Product product = productUseCase.findByCode(productCode)
657
- .orElseThrow(() -> new ProductNotFoundException(productCode));
658
- model.addAttribute("form", ProductForm.from(product));
659
- model.addAttribute("categories", ProductCategory.values());
660
- return "products/edit";
661
- }
662
-
663
- @PostMapping("/{productCode}")
664
- public String update(
665
- @PathVariable String productCode,
666
- @Valid @ModelAttribute("form") ProductForm form,
667
- BindingResult bindingResult,
668
- RedirectAttributes redirectAttributes,
669
- Model model) {
670
-
671
- if (bindingResult.hasErrors()) {
672
- model.addAttribute("categories", ProductCategory.values());
673
- return "products/edit";
674
- }
675
-
676
- productUseCase.updateProduct(productCode, form.toCommand());
677
- redirectAttributes.addFlashAttribute("successMessage", "商品を更新しました");
678
- return "redirect:/products/" + productCode;
679
- }
680
-
681
- @PostMapping("/{productCode}/delete")
682
- public String delete(@PathVariable String productCode, RedirectAttributes redirectAttributes) {
683
- productUseCase.deleteProduct(productCode);
684
- redirectAttributes.addFlashAttribute("successMessage", "商品を削除しました");
685
- return "redirect:/products";
686
- }
687
- }
688
- ```
689
-
690
- </details>
691
-
692
- ---
693
-
694
- ## 第16章:トランザクションの実装
695
-
696
- ### 16.1 受注業務画面の実装
697
-
698
- #### 受注 Controller
699
-
700
- <details>
701
- <summary>コード例: OrderController.java</summary>
702
-
703
- ```java
704
- @Controller
705
- @RequestMapping("/orders")
706
- public class OrderController {
707
-
708
- private final OrderUseCase orderUseCase;
709
- private final ProductUseCase productUseCase;
710
- private final PartnerUseCase partnerUseCase;
711
-
712
- @GetMapping
713
- public String list(
714
- @RequestParam(required = false) String status,
715
- @RequestParam(required = false) String customerCode,
716
- Model model) {
717
-
718
- List<Order> orders = orderUseCase.getOrders(status, customerCode);
719
- model.addAttribute("orders", orders);
720
- model.addAttribute("customers", partnerUseCase.getCustomers());
721
- return "orders/list";
722
- }
723
-
724
- @GetMapping("/new")
725
- public String newForm(Model model) {
726
- model.addAttribute("form", new OrderForm());
727
- model.addAttribute("customers", partnerUseCase.getCustomers());
728
- model.addAttribute("products", productUseCase.getAllProducts());
729
- return "orders/new";
730
- }
731
-
732
- @PostMapping
733
- public String create(
734
- @Valid @ModelAttribute("form") OrderForm form,
735
- BindingResult bindingResult,
736
- Model model,
737
- RedirectAttributes redirectAttributes) {
738
-
739
- if (bindingResult.hasErrors()) {
740
- model.addAttribute("customers", partnerUseCase.getCustomers());
741
- model.addAttribute("products", productUseCase.getAllProducts());
742
- return "orders/new";
743
- }
744
-
745
- Order order = orderUseCase.createOrder(form.toCommand());
746
- redirectAttributes.addFlashAttribute("successMessage",
747
- "受注「" + order.getOrderNumber() + "」を登録しました");
748
- return "redirect:/orders";
749
- }
750
-
751
- @PostMapping("/{orderNumber}/confirm")
752
- public String confirm(@PathVariable String orderNumber, RedirectAttributes redirectAttributes) {
753
- orderUseCase.confirmOrder(orderNumber);
754
- redirectAttributes.addFlashAttribute("successMessage",
755
- "受注「" + orderNumber + "」を確定しました");
756
- return "redirect:/orders/" + orderNumber;
757
- }
758
-
759
- @PostMapping("/{orderNumber}/cancel")
760
- public String cancel(@PathVariable String orderNumber, RedirectAttributes redirectAttributes) {
761
- orderUseCase.cancelOrder(orderNumber);
762
- redirectAttributes.addFlashAttribute("successMessage",
763
- "受注「" + orderNumber + "」を取り消しました");
764
- return "redirect:/orders";
765
- }
766
- }
767
- ```
768
-
769
- </details>
770
-
771
- ### 16.2 動的フォーム(明細行の追加・削除)
772
-
773
- htmx を使用して、ページをリロードせずに明細行を追加・削除できます。
774
-
775
- <details>
776
- <summary>コード例: 動的フォーム(明細行)</summary>
777
-
778
- ```html
779
- <!-- 明細追加ボタン -->
780
- <button type="button" class="btn btn-sm btn-outline-primary"
781
- hx-get="/orders/add-detail-row"
782
- hx-target="#detail-rows"
783
- hx-swap="beforeend"
784
- hx-vals='js:{"index": document.querySelectorAll("#detail-rows tr").length}'>
785
- <i class="bi bi-plus"></i> 行追加
786
- </button>
787
-
788
- <!-- 明細行フラグメント -->
789
- <tr th:fragment="detailRow" th:id="'row-' + ${index}">
790
- <td>
791
- <select class="form-select product-select"
792
- th:name="'details[' + ${index} + '].productCode'" required
793
- onchange="updatePrice(this)">
794
- <option value="">選択</option>
795
- <option th:each="product : ${products}"
796
- th:value="${product.productCode}"
797
- th:text="${product.productCode + ' - ' + product.productName}"
798
- th:data-price="${product.unitPrice}"></option>
799
- </select>
800
- </td>
801
- <td>
802
- <input type="number" class="form-control quantity-input"
803
- th:name="'details[' + ${index} + '].quantity'" min="1" value="1" required
804
- onchange="calculateAmount(this)">
805
- </td>
806
- <td>
807
- <input type="number" class="form-control price-input"
808
- th:name="'details[' + ${index} + '].unitPrice'" step="1" min="0"
809
- onchange="calculateAmount(this)">
810
- </td>
811
- <td>
812
- <input type="text" class="form-control amount-display" readonly value="0">
813
- </td>
814
- <td>
815
- <button type="button" class="btn btn-sm btn-outline-danger"
816
- onclick="this.closest('tr').remove(); calculateTotal()">削除</button>
817
- </td>
818
- </tr>
819
- ```
820
-
821
- </details>
822
-
823
- ### 16.3 出荷業務画面の実装
824
-
825
- <details>
826
- <summary>コード例: ShipmentController.java</summary>
827
-
828
- ```java
829
- @Controller
830
- @RequestMapping("/shipments")
831
- public class ShipmentController {
832
-
833
- private final ShipmentUseCase shipmentUseCase;
834
- private final OrderUseCase orderUseCase;
835
-
836
- @GetMapping
837
- public String list(
838
- @RequestParam(required = false) String status,
839
- @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate shipmentDate,
840
- Model model) {
841
-
842
- List<Shipment> shipments = shipmentDate != null
843
- ? shipmentUseCase.getShipmentsByDate(shipmentDate)
844
- : shipmentUseCase.getShipments(status);
845
-
846
- model.addAttribute("shipments", shipments);
847
- model.addAttribute("today", LocalDate.now());
848
- return "shipments/list";
849
- }
850
-
851
- @PostMapping("/{shipmentNumber}/execute")
852
- public String execute(@PathVariable String shipmentNumber, RedirectAttributes redirectAttributes) {
853
- try {
854
- shipmentUseCase.executeShipment(shipmentNumber);
855
- redirectAttributes.addFlashAttribute("successMessage",
856
- "出荷「" + shipmentNumber + "」を実行しました");
857
- } catch (InsufficientInventoryException e) {
858
- redirectAttributes.addFlashAttribute("errorMessage",
859
- "在庫不足のため出荷できません: " + e.getMessage());
860
- }
861
- return "redirect:/shipments/" + shipmentNumber;
862
- }
863
- }
864
- ```
865
-
866
- </details>
867
-
868
- ---
869
-
870
- ## 第17章:エラーハンドリングと帳票出力
871
-
872
- ### 17.1 グローバル例外ハンドラ
873
-
874
- <details>
875
- <summary>コード例: GlobalExceptionHandler.java</summary>
876
-
877
- ```java
878
- @ControllerAdvice
879
- public class GlobalExceptionHandler {
880
-
881
- private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
882
-
883
- /**
884
- * 業務エラー(リソース未検出)
885
- */
886
- @ExceptionHandler(ResourceNotFoundException.class)
887
- public String handleResourceNotFound(ResourceNotFoundException ex, Model model) {
888
- logger.warn("リソース未検出: {}", ex.getMessage());
889
- model.addAttribute("errorMessage", ex.getMessage());
890
- return "error/404";
891
- }
892
-
893
- /**
894
- * 業務エラー(バリデーション)
895
- */
896
- @ExceptionHandler(BusinessValidationException.class)
897
- public String handleBusinessValidation(BusinessValidationException ex, Model model) {
898
- logger.warn("業務バリデーションエラー: {}", ex.getMessage());
899
- model.addAttribute("errorMessage", ex.getMessage());
900
- model.addAttribute("errorCode", ex.getErrorCode());
901
- return "error/business";
902
- }
903
-
904
- /**
905
- * システムエラー
906
- */
907
- @ExceptionHandler(Exception.class)
908
- public String handleGeneralException(Exception ex, Model model) {
909
- logger.error("システムエラー", ex);
910
- model.addAttribute("errorMessage", "システムエラーが発生しました。管理者にお問い合わせください。");
911
- return "error/500";
912
- }
913
- }
914
- ```
915
-
916
- </details>
917
-
918
- ### 17.2 Excel 帳票出力
919
-
920
- <details>
921
- <summary>コード例: ReportController.java(Excel 出力)</summary>
922
-
923
- ```java
924
- @Controller
925
- @RequestMapping("/reports")
926
- public class ReportController {
927
-
928
- private final ReportUseCase reportUseCase;
929
-
930
- @GetMapping("/inventory/excel")
931
- public void exportInventoryExcel(HttpServletResponse response) throws IOException {
932
- response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
933
- response.setHeader("Content-Disposition",
934
- "attachment; filename=inventory_" + LocalDate.now() + ".xlsx");
935
-
936
- try (Workbook workbook = new XSSFWorkbook()) {
937
- Sheet sheet = workbook.createSheet("在庫一覧");
938
-
939
- // ヘッダー行
940
- Row headerRow = sheet.createRow(0);
941
- CellStyle headerStyle = createHeaderStyle(workbook);
942
- String[] headers = {"商品コード", "商品名", "現在在庫", "安全在庫", "過不足"};
943
- for (int i = 0; i < headers.length; i++) {
944
- Cell cell = headerRow.createCell(i);
945
- cell.setCellValue(headers[i]);
946
- cell.setCellStyle(headerStyle);
947
- }
948
-
949
- // データ行
950
- List<InventoryData> inventoryList = reportUseCase.getInventoryReport();
951
- int rowNum = 1;
952
- for (InventoryData data : inventoryList) {
953
- Row row = sheet.createRow(rowNum++);
954
- row.createCell(0).setCellValue(data.getProductCode());
955
- row.createCell(1).setCellValue(data.getProductName());
956
- row.createCell(2).setCellValue(data.getCurrentStock());
957
- row.createCell(3).setCellValue(data.getSafetyStock());
958
- row.createCell(4).setCellValue(data.getDifference());
959
- }
960
-
961
- // 列幅自動調整
962
- for (int i = 0; i < headers.length; i++) {
963
- sheet.autoSizeColumn(i);
964
- }
965
-
966
- workbook.write(response.getOutputStream());
967
- }
968
- }
969
- }
970
- ```
971
-
972
- </details>
973
-
974
- ### 17.3 PDF 帳票出力(Thymeleaf + OpenHTMLtoPDF)
975
-
976
- PDF 帳票は **Thymeleaf テンプレート + OpenHTMLtoPDF** を使用して実装します。この方式により、HTML/CSS で帳票のレイアウトを定義でき、デザインの自由度が高く保守性も向上します。
977
-
978
- ```plantuml
979
- @startuml pdf_generation_flow
980
- title PDF 生成フロー
981
-
982
- participant "Controller" as C
983
- participant "PdfGeneratorService" as PDF
984
- participant "TemplateEngine" as T
985
- participant "PdfRendererBuilder" as R
986
-
987
- C -> PDF : generatePdf(templateName, variables)
988
- PDF -> T : process(templateName, context)
989
- T --> PDF : HTML 文字列
990
- PDF -> R : withHtmlContent(html, null)
991
- R -> R : run()
992
- R --> PDF : PDF バイナリ
993
- PDF --> C : byte[]
994
-
995
- @enduml
996
- ```
997
-
998
- #### PDF 生成サービス
999
-
1000
- <details>
1001
- <summary>コード例: PdfGeneratorService.java</summary>
1002
-
1003
- ```java
1004
- package com.example.sms.infrastructure.in.web.service;
1005
-
1006
- import com.openhtmltopdf.pdfboxout.PdfRendererBuilder;
1007
- import org.springframework.core.io.ClassPathResource;
1008
- import org.springframework.stereotype.Service;
1009
- import org.thymeleaf.TemplateEngine;
1010
- import org.thymeleaf.context.Context;
1011
-
1012
- import java.io.ByteArrayOutputStream;
1013
- import java.io.File;
1014
- import java.io.IOException;
1015
- import java.util.Map;
1016
-
1017
- /**
1018
- * PDF生成サービス.
1019
- * ThymeleafテンプレートからPDFを生成する.
1020
- */
1021
- @Service
1022
- public class PdfGeneratorService {
1023
-
1024
- private static final String FONT_FAMILY = "Japanese";
1025
-
1026
- /** Windowsのシステムフォントパス. */
1027
- private static final String[] WINDOWS_FONTS = {
1028
- "C:/Windows/Fonts/YuGothM.ttc", // Yu Gothic Medium
1029
- "C:/Windows/Fonts/YuGothR.ttc", // Yu Gothic Regular
1030
- "C:/Windows/Fonts/msgothic.ttc", // MS Gothic
1031
- "C:/Windows/Fonts/meiryo.ttc" // Meiryo
1032
- };
1033
-
1034
- private final TemplateEngine templateEngine;
1035
- private final File japaneseFontFile;
1036
-
1037
- public PdfGeneratorService(TemplateEngine templateEngine) {
1038
- this.templateEngine = templateEngine;
1039
- this.japaneseFontFile = findJapaneseFont();
1040
- }
1041
-
1042
- /**
1043
- * ThymeleafテンプレートからPDFを生成する.
1044
- */
1045
- public byte[] generatePdf(String templateName, Map<String, Object> variables) {
1046
- Context context = new Context();
1047
- context.setVariables(variables);
1048
- String html = templateEngine.process(templateName, context);
1049
-
1050
- try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
1051
- PdfRendererBuilder builder = new PdfRendererBuilder()
1052
- .useFastMode();
1053
-
1054
- // 日本語フォントを登録
1055
- if (japaneseFontFile != null) {
1056
- builder = builder.useFont(japaneseFontFile, FONT_FAMILY);
1057
- }
1058
-
1059
- builder.withHtmlContent(html, null)
1060
- .toStream(outputStream)
1061
- .run();
1062
-
1063
- return outputStream.toByteArray();
1064
- } catch (IOException e) {
1065
- throw new PdfGenerationException("PDF生成に失敗しました", e);
1066
- }
1067
- }
1068
-
1069
- /** 日本語フォントを検索する. */
1070
- private File findJapaneseFont() {
1071
- // クラスパスからフォントを検索
1072
- ClassPathResource fontResource = new ClassPathResource("fonts/NotoSansJP-Regular.ttf");
1073
- if (fontResource.exists()) {
1074
- try {
1075
- return fontResource.getFile();
1076
- } catch (IOException e) {
1077
- // クラスパスからの読み込み失敗時はシステムフォントを試す
1078
- }
1079
- }
1080
-
1081
- // Windowsのシステムフォントを検索
1082
- for (String fontPath : WINDOWS_FONTS) {
1083
- File fontFile = new File(fontPath);
1084
- if (fontFile.exists()) {
1085
- return fontFile;
1086
- }
1087
- }
1088
- return null;
1089
- }
1090
- }
1091
- ```
1092
-
1093
- </details>
1094
-
1095
- **日本語フォント対応のポイント:**
1096
-
1097
- | ポイント | 説明 |
1098
- |---------|------|
1099
- | **フォント登録** | `builder.useFont(fontFile, fontFamily)` でフォントを登録 |
1100
- | **CSS でフォント指定** | `font-family: Japanese, sans-serif;` で登録したフォントを参照 |
1101
- | **システムフォント** | Windows は Yu Gothic、macOS はヒラギノなどを自動検出 |
1102
- | **カスタムフォント** | `resources/fonts/` に TTF ファイルを配置して使用可能 |
1103
-
1104
- #### PDF 用 HTML テンプレート
1105
-
1106
- <details>
1107
- <summary>コード例: reports/invoice-pdf.html</summary>
1108
-
1109
- ```html
1110
- <!DOCTYPE html>
1111
- <html xmlns:th="http://www.thymeleaf.org" lang="ja">
1112
- <head>
1113
- <meta charset="UTF-8"/>
1114
- <title>請求書</title>
1115
- <style>
1116
- @page {
1117
- size: A4;
1118
- margin: 20mm;
1119
- }
1120
- body {
1121
- /* "Japanese" は PdfGeneratorService で登録したフォントファミリー名 */
1122
- font-family: Japanese, "Noto Sans JP", "Hiragino Sans", sans-serif;
1123
- font-size: 10pt;
1124
- line-height: 1.5;
1125
- }
1126
- .header {
1127
- text-align: center;
1128
- margin-bottom: 30px;
1129
- }
1130
- .header h1 {
1131
- font-size: 24pt;
1132
- margin: 0;
1133
- }
1134
- .amount-box {
1135
- border: 2px solid #333;
1136
- padding: 15px;
1137
- margin-bottom: 30px;
1138
- text-align: center;
1139
- }
1140
- .amount-box .value {
1141
- font-size: 20pt;
1142
- font-weight: bold;
1143
- }
1144
- table {
1145
- width: 100%;
1146
- border-collapse: collapse;
1147
- margin-bottom: 20px;
1148
- }
1149
- th, td {
1150
- border: 1px solid #333;
1151
- padding: 8px;
1152
- }
1153
- th {
1154
- background-color: #f0f0f0;
1155
- text-align: center;
1156
- }
1157
- td.number {
1158
- text-align: right;
1159
- }
1160
- </style>
1161
- </head>
1162
- <body>
1163
- <div class="header">
1164
- <h1>請求書</h1>
1165
- </div>
1166
-
1167
- <div class="meta">
1168
- <p>請求番号: <span th:text="${invoice.invoiceNumber}"></span></p>
1169
- <p>請求日: <span th:text="${#temporals.format(invoice.invoiceDate, 'yyyy年MM月dd日')}"></span></p>
1170
- </div>
1171
-
1172
- <div class="customer">
1173
- <p><span th:text="${customerName}"></span> 御中</p>
1174
- </div>
1175
-
1176
- <div class="amount-box">
1177
- <p class="label">ご請求金額</p>
1178
- <p class="value">¥<span th:text="${#numbers.formatDecimal(invoice.currentInvoiceAmount, 1, 'COMMA', 0, 'POINT')}"></span>-</p>
1179
- </div>
1180
-
1181
- <!-- 請求明細テーブル -->
1182
- <table th:if="${invoice.details != null and !invoice.details.isEmpty()}">
1183
- <thead>
1184
- <tr>
1185
- <th>売上番号</th>
1186
- <th>売上日</th>
1187
- <th>売上金額</th>
1188
- <th>消費税</th>
1189
- <th>合計</th>
1190
- </tr>
1191
- </thead>
1192
- <tbody>
1193
- <tr th:each="detail : ${invoice.details}">
1194
- <td th:text="${detail.salesNumber}"></td>
1195
- <td th:text="${#temporals.format(detail.salesDate, 'yyyy/MM/dd')}"></td>
1196
- <td class="number" th:text="${#numbers.formatDecimal(detail.salesAmount, 1, 'COMMA', 0, 'POINT')}"></td>
1197
- <td class="number" th:text="${#numbers.formatDecimal(detail.taxAmount, 1, 'COMMA', 0, 'POINT')}"></td>
1198
- <td class="number" th:text="${#numbers.formatDecimal(detail.totalAmount, 1, 'COMMA', 0, 'POINT')}"></td>
1199
- </tr>
1200
- </tbody>
1201
- </table>
1202
- </body>
1203
- </html>
1204
- ```
1205
-
1206
- </details>
1207
-
1208
- #### Controller での PDF 出力
1209
-
1210
- <details>
1211
- <summary>コード例: ReportWebController.java(PDF 出力)</summary>
1212
-
1213
- ```java
1214
- @Controller
1215
- @RequestMapping("/reports")
1216
- public class ReportWebController {
1217
-
1218
- private final ReportService reportService;
1219
- private final PdfGeneratorService pdfGeneratorService;
1220
-
1221
- /**
1222
- * 請求書を PDF 形式でエクスポート.
1223
- */
1224
- @GetMapping("/invoice/{invoiceNumber}/pdf")
1225
- public void exportInvoicePdf(@PathVariable String invoiceNumber, HttpServletResponse response)
1226
- throws IOException {
1227
-
1228
- Invoice invoice = reportService.getInvoiceForReport(invoiceNumber);
1229
- String customerName = reportService.getCustomerName(invoice.getCustomerCode());
1230
-
1231
- // テンプレート変数を設定
1232
- Map<String, Object> variables = Map.of(
1233
- "invoice", invoice,
1234
- "customerName", customerName
1235
- );
1236
-
1237
- // PDFを生成
1238
- byte[] pdfBytes = pdfGeneratorService.generatePdf("reports/invoice-pdf", variables);
1239
-
1240
- // レスポンスを設定
1241
- response.setContentType("application/pdf");
1242
- response.setHeader("Content-Disposition",
1243
- "attachment; filename=invoice_" + invoiceNumber + ".pdf");
1244
- response.setContentLength(pdfBytes.length);
1245
- response.getOutputStream().write(pdfBytes);
1246
- }
1247
- }
1248
- ```
1249
-
1250
- </details>
1251
-
1252
- #### Thymeleaf + OpenHTMLtoPDF のメリット
1253
-
1254
- | 観点 | 説明 |
1255
- |------|------|
1256
- | **デザインの自由度** | HTML/CSS でレイアウトを定義できるため、複雑なデザインも容易 |
1257
- | **保守性** | HTML テンプレートなので、プログラマー以外でも修正しやすい |
1258
- | **再利用** | 同じテンプレートを Web 表示とPDF 出力で共有可能 |
1259
- | **プレビュー** | ブラウザで HTML として確認後、PDF 出力できる |
1260
- | **日本語対応** | CSS で日本語フォントを指定するだけで対応可能 |
1261
-
1262
- ---
1263
-
1264
- ## 第18章:ページネーション
1265
-
1266
- ### 18.1 ページネーションの概要
1267
-
1268
- 大量のデータを扱う一覧画面では、パフォーマンスとユーザビリティの観点から**ページネーション**(ページ分割)が必要です。本章では、商品マスタ画面を例に、ヘキサゴナルアーキテクチャに沿ったページネーション機能の実装方法を解説します。
1269
-
1270
- ```plantuml
1271
- @startuml pagination_flow
1272
- title ページネーション処理フロー
1273
-
1274
- actor "ブラウザ" as Browser
1275
- participant "ProductWebController" as Controller
1276
- participant "ProductService" as Service
1277
- participant "ProductRepositoryImpl" as Repository
1278
- participant "ProductMapper" as Mapper
1279
- database "PostgreSQL" as DB
1280
-
1281
- Browser -> Controller : GET /products?page=0&size=10
1282
- Controller -> Service : getProducts(page, size, category, keyword)
1283
- Service -> Repository : findWithPagination(page, size, category, keyword)
1284
- Repository -> Mapper : findWithPagination(offset, limit, category, keyword)
1285
- Mapper -> DB : SELECT ... LIMIT #{limit} OFFSET #{offset}
1286
- DB --> Mapper : 商品データ(10件)
1287
- Repository -> Mapper : count(category, keyword)
1288
- Mapper -> DB : SELECT COUNT(*) ...
1289
- DB --> Mapper : 総件数
1290
- Repository --> Service : PageResult<Product>
1291
- Service --> Controller : PageResult<Product>
1292
- Controller -> Browser : products/list.html\n(ページネーション UI 付き)
1293
-
1294
- @enduml
1295
- ```
1296
-
1297
- ### 18.2 ページネーション結果クラス
1298
-
1299
- ページネーション結果を表す汎用クラスを定義します。
1300
-
1301
- <details>
1302
- <summary>コード例: PageResult.java</summary>
1303
-
1304
- ```java
1305
- package com.example.sms.domain.model.common;
1306
-
1307
- import java.util.List;
1308
-
1309
- /**
1310
- * ページネーション結果を表すクラス.
1311
- *
1312
- * @param <T> 要素の型
1313
- */
1314
- public class PageResult<T> {
1315
-
1316
- private final List<T> content;
1317
- private final int pageNumber;
1318
- private final int pageSize;
1319
- private final long totalElements;
1320
- private final int totalPages;
1321
-
1322
- public PageResult(List<T> content, int pageNumber, int pageSize, long totalElements) {
1323
- this.content = content;
1324
- this.pageNumber = pageNumber;
1325
- this.pageSize = pageSize;
1326
- this.totalElements = totalElements;
1327
- this.totalPages = pageSize > 0 ? (int) Math.ceil((double) totalElements / pageSize) : 0;
1328
- }
1329
-
1330
- public List<T> getContent() { return content; }
1331
- public int getPage() { return pageNumber; }
1332
- public int getSize() { return pageSize; }
1333
- public long getTotalElements() { return totalElements; }
1334
- public int getTotalPages() { return totalPages; }
1335
- public boolean hasNext() { return pageNumber < totalPages - 1; }
1336
- public boolean hasPrevious() { return pageNumber > 0; }
1337
- public boolean isFirst() { return pageNumber == 0; }
1338
- public boolean isLast() { return pageNumber >= totalPages - 1; }
1339
- public int getNumber() { return pageNumber + 1; }
1340
-
1341
- public static <T> PageResult<T> empty() {
1342
- return new PageResult<>(List.of(), 0, 10, 0);
1343
- }
1344
- }
1345
- ```
1346
-
1347
- </details>
1348
-
1349
- ### 18.3 MyBatis マッパーの実装
1350
-
1351
- ページネーション用の SQL クエリを追加します。`LIMIT` と `OFFSET` を使用してデータを取得し、`COUNT` で総件数を取得します。
1352
-
1353
- <details>
1354
- <summary>コード例: ProductMapper.xml(抜粋)</summary>
1355
-
1356
- ```xml
1357
- <select id="findWithPagination" resultMap="ProductResultMap">
1358
- SELECT * FROM "商品マスタ"
1359
- <where>
1360
- <if test="category != null and category != ''">
1361
- "商品区分" = #{category}::商品区分
1362
- </if>
1363
- <if test="keyword != null and keyword != ''">
1364
- AND (LOWER("商品コード") LIKE LOWER('%' || #{keyword} || '%')
1365
- OR LOWER("商品名") LIKE LOWER('%' || #{keyword} || '%'))
1366
- </if>
1367
- </where>
1368
- ORDER BY "商品コード"
1369
- LIMIT #{limit} OFFSET #{offset}
1370
- </select>
1371
-
1372
- <select id="count" resultType="long">
1373
- SELECT COUNT(*) FROM "商品マスタ"
1374
- <where>
1375
- <if test="category != null and category != ''">
1376
- "商品区分" = #{category}::商品区分
1377
- </if>
1378
- <if test="keyword != null and keyword != ''">
1379
- AND (LOWER("商品コード") LIKE LOWER('%' || #{keyword} || '%')
1380
- OR LOWER("商品名") LIKE LOWER('%' || #{keyword} || '%'))
1381
- </if>
1382
- </where>
1383
- </select>
1384
- ```
1385
-
1386
- </details>
1387
-
1388
- <details>
1389
- <summary>コード例: ProductMapper.java</summary>
1390
-
1391
- ```java
1392
- @Mapper
1393
- public interface ProductMapper {
1394
- // 既存のメソッド...
1395
-
1396
- List<Product> findWithPagination(
1397
- @Param("offset") int offset,
1398
- @Param("limit") int limit,
1399
- @Param("category") String category,
1400
- @Param("keyword") String keyword);
1401
-
1402
- long count(@Param("category") String category, @Param("keyword") String keyword);
1403
- }
1404
- ```
1405
-
1406
- </details>
1407
-
1408
- ### 18.4 リポジトリの実装
1409
-
1410
- Output Port にページネーション用のメソッドを追加します。
1411
-
1412
- <details>
1413
- <summary>コード例: ProductRepository.java(インターフェース)</summary>
1414
-
1415
- ```java
1416
- public interface ProductRepository {
1417
- // 既存のメソッド...
1418
-
1419
- /**
1420
- * ページネーション付きで商品を取得.
1421
- */
1422
- PageResult<Product> findWithPagination(int page, int size, ProductCategory category, String keyword);
1423
- }
1424
- ```
1425
-
1426
- </details>
1427
-
1428
- <details>
1429
- <summary>コード例: ProductRepositoryImpl.java(実装)</summary>
1430
-
1431
- ```java
1432
- @Override
1433
- public PageResult<Product> findWithPagination(int page, int size, ProductCategory category, String keyword) {
1434
- int offset = page * size;
1435
- String categoryName = category != null ? category.getDisplayName() : null;
1436
-
1437
- List<Product> products = productMapper.findWithPagination(offset, size, categoryName, keyword);
1438
- long totalElements = productMapper.count(categoryName, keyword);
1439
-
1440
- return new PageResult<>(products, page, size, totalElements);
1441
- }
1442
- ```
1443
-
1444
- </details>
1445
-
1446
- ### 18.5 ユースケースとサービスの実装
1447
-
1448
- Input Port にページネーション用のメソッドを追加します。
1449
-
1450
- <details>
1451
- <summary>コード例: ProductUseCase.java</summary>
1452
-
1453
- ```java
1454
- public interface ProductUseCase {
1455
- // 既存のメソッド...
1456
-
1457
- /**
1458
- * ページネーション付きで商品を取得する.
1459
- */
1460
- PageResult<Product> getProducts(int page, int size, ProductCategory category, String keyword);
1461
- }
1462
- ```
1463
-
1464
- </details>
1465
-
1466
- <details>
1467
- <summary>コード例: ProductService.java</summary>
1468
-
1469
- ```java
1470
- @Override
1471
- @Transactional(readOnly = true)
1472
- public PageResult<Product> getProducts(int page, int size, ProductCategory category, String keyword) {
1473
- return productRepository.findWithPagination(page, size, category, keyword);
1474
- }
1475
- ```
1476
-
1477
- </details>
1478
-
1479
- ### 18.6 Controller の実装
1480
-
1481
- ページネーションパラメータを受け取り、ビューにページ情報を渡します。
1482
-
1483
- <details>
1484
- <summary>コード例: ProductWebController.java</summary>
1485
-
1486
- ```java
1487
- @Controller
1488
- @RequestMapping("/products")
1489
- public class ProductWebController {
1490
-
1491
- private static final int DEFAULT_PAGE_SIZE = 10;
1492
-
1493
- private final ProductUseCase productUseCase;
1494
- // ...
1495
-
1496
- @GetMapping
1497
- public String list(
1498
- @RequestParam(defaultValue = "0") int page,
1499
- @RequestParam(defaultValue = "10") int size,
1500
- @RequestParam(required = false) ProductCategory category,
1501
- @RequestParam(required = false) String keyword,
1502
- Model model) {
1503
-
1504
- PageResult<Product> productPage = productUseCase.getProducts(page, size, category, keyword);
1505
-
1506
- model.addAttribute("products", productPage.getContent());
1507
- model.addAttribute("page", productPage);
1508
- model.addAttribute("categories", ProductCategory.values());
1509
- model.addAttribute("selectedCategory", category);
1510
- model.addAttribute("keyword", keyword);
1511
- model.addAttribute("currentSize", size);
1512
- return "products/list";
1513
- }
1514
- }
1515
- ```
1516
-
1517
- </details>
1518
-
1519
- ### 18.7 Thymeleaf テンプレートの実装
1520
-
1521
- Bootstrap のページネーションコンポーネントを使用して UI を実装します。
1522
-
1523
- <details>
1524
- <summary>コード例: products/list.html(ページネーション部分)</summary>
1525
-
1526
- ```html
1527
- <!-- 検索フォームに表示件数選択を追加 -->
1528
- <div class="col-md-2">
1529
- <label class="form-label">表示件数</label>
1530
- <select name="size" class="form-select">
1531
- <option value="10" th:selected="${currentSize == 10}">10件</option>
1532
- <option value="25" th:selected="${currentSize == 25}">25件</option>
1533
- <option value="50" th:selected="${currentSize == 50}">50件</option>
1534
- <option value="100" th:selected="${currentSize == 100}">100件</option>
1535
- </select>
1536
- </div>
1537
-
1538
- <!-- ページネーション -->
1539
- <div class="mt-3">
1540
- <div class="text-muted text-center mb-2">
1541
- <span th:if="${page.totalElements > 0}">
1542
- <span th:text="${page.page * page.size + 1}">1</span> -
1543
- <span th:text="${page.page * page.size + #lists.size(products)}">10</span> 件
1544
- (全 <span th:text="${page.totalElements}">0</span> 件)
1545
- </span>
1546
- <span th:if="${page.totalElements == 0}">0 件</span>
1547
- </div>
1548
-
1549
- <nav th:if="${page.totalPages > 1}" aria-label="ページナビゲーション">
1550
- <ul class="pagination justify-content-center mb-0">
1551
- <!-- 最初のページへ -->
1552
- <li class="page-item" th:classappend="${page.first} ? 'disabled'">
1553
- <a class="page-link" th:href="@{/products(page=0, size=${currentSize},
1554
- category=${selectedCategory?.name()}, keyword=${keyword})}">&laquo;</a>
1555
- </li>
1556
- <!-- 前のページへ -->
1557
- <li class="page-item" th:classappend="${!page.hasPrevious()} ? 'disabled'">
1558
- <a class="page-link" th:href="@{/products(page=${page.page - 1},
1559
- size=${currentSize}, category=${selectedCategory?.name()},
1560
- keyword=${keyword})}">&lsaquo;</a>
1561
- </li>
1562
-
1563
- <!-- ページ番号 -->
1564
- <th:block th:with="startPage=${page.page > 2 ? page.page - 2 : 0},
1565
- endPage=${page.page + 2 < page.totalPages - 1 ? page.page + 2 : page.totalPages - 1}">
1566
- <li class="page-item" th:if="${startPage > 0}">
1567
- <span class="page-link">...</span>
1568
- </li>
1569
- <li th:each="i : ${#numbers.sequence(startPage, endPage)}"
1570
- class="page-item"
1571
- th:classappend="${i == page.page} ? 'active'">
1572
- <a class="page-link"
1573
- th:href="@{/products(page=${i}, size=${currentSize},
1574
- category=${selectedCategory?.name()}, keyword=${keyword})}"
1575
- th:text="${i + 1}">1</a>
1576
- </li>
1577
- <li class="page-item" th:if="${endPage < page.totalPages - 1}">
1578
- <span class="page-link">...</span>
1579
- </li>
1580
- </th:block>
1581
-
1582
- <!-- 次のページへ -->
1583
- <li class="page-item" th:classappend="${!page.hasNext()} ? 'disabled'">
1584
- <a class="page-link" th:href="@{/products(page=${page.page + 1},
1585
- size=${currentSize}, category=${selectedCategory?.name()},
1586
- keyword=${keyword})}">&rsaquo;</a>
1587
- </li>
1588
- <!-- 最後のページへ -->
1589
- <li class="page-item" th:classappend="${page.last} ? 'disabled'">
1590
- <a class="page-link" th:href="@{/products(page=${page.totalPages - 1},
1591
- size=${currentSize}, category=${selectedCategory?.name()},
1592
- keyword=${keyword})}">&raquo;</a>
1593
- </li>
1594
- </ul>
1595
- </nav>
1596
- </div>
1597
- ```
1598
-
1599
- </details>
1600
-
1601
- ### 18.8 ページネーションの設計ポイント
1602
-
1603
- | ポイント | 説明 |
1604
- |---------|------|
1605
- | **0始まり vs 1始まり** | 内部では0始まり、表示では1始まりを使用 |
1606
- | **デフォルト値** | ページサイズは 10 件程度が一般的 |
1607
- | **検索条件の保持** | ページ遷移時も検索条件(カテゴリ、キーワード)を保持 |
1608
- | **ページ番号の省略** | 大量ページの場合は「...」で省略 |
1609
- | **SQL 最適化** | OFFSET が大きい場合のパフォーマンスに注意 |
1610
-
1611
- ### 18.9 パフォーマンス考慮事項
1612
-
1613
- ```plantuml
1614
- @startuml offset_performance
1615
- title OFFSET のパフォーマンス問題
1616
-
1617
- note as N1
1618
- **OFFSET が大きい場合の問題**
1619
-
1620
- SELECT * FROM products
1621
- ORDER BY product_code
1622
- LIMIT 10 OFFSET 100000
1623
-
1624
- → 先頭から100,000件スキャンしてから
1625
- 10件を返す(非効率)
1626
-
1627
- **対策**
1628
- 1. Keyset Pagination(カーソル方式)
1629
- 2. インデックス付きカラムでの絞り込み
1630
- 3. キャッシュの活用
1631
- end note
1632
-
1633
- @enduml
1634
- ```
1635
-
1636
- 大量データの場合は、OFFSET ベースではなく **Keyset Pagination**(カーソル方式)を検討します:
1637
-
1638
- ```sql
1639
- -- OFFSET 方式(非効率)
1640
- SELECT * FROM products ORDER BY product_code LIMIT 10 OFFSET 100000;
1641
-
1642
- -- Keyset Pagination(効率的)
1643
- SELECT * FROM products
1644
- WHERE product_code > 'LAST_SEEN_CODE'
1645
- ORDER BY product_code
1646
- LIMIT 10;
1647
- ```
1648
-
1649
- ---
1650
-
1651
- ## 第10部-B のまとめ
1652
-
1653
- ### API サーバー版との比較
1654
-
1655
- | 観点 | API サーバー版 | モノリス版 |
1656
- |------|--------------|-----------|
1657
- | **アーキテクチャ** | フロントエンド分離(SPA) | 統合(Thymeleaf) |
1658
- | **通信** | REST API(JSON) | サーバーサイドレンダリング(HTML) |
1659
- | **状態管理** | クライアント側(localStorage 等) | サーバー側(セッション) |
1660
- | **部分更新** | JavaScript(Axios 等) | htmx |
1661
- | **帳票出力** | API 経由でダウンロード | 直接ダウンロード |
1662
- | **認証** | JWT/OAuth | セッションベース |
1663
- | **複雑さ** | 高(API 設計、CORS 等) | 低(シンプル) |
1664
- | **開発速度** | 初期は遅い | 初期は速い |
1665
-
1666
- ### 実装した機能
1667
-
1668
- | カテゴリ | 機能 |
1669
- |---------|------|
1670
- | **商品マスタ** | 一覧、詳細、登録、編集、削除、検索、ページネーション |
1671
- | **取引先マスタ** | 一覧、詳細(取引履歴)、登録、編集、削除、検索 |
1672
- | **受注** | 一覧、登録(動的明細)、確定、取消 |
1673
- | **出荷** | 一覧、出荷指示、出荷実行、取消 |
1674
- | **請求** | 一覧、締処理、請求書発行 |
1675
- | **入金** | 一覧、入金登録、消込処理 |
1676
- | **帳票** | Excel/PDF 出力 |
1677
- | **共通** | レイアウト、エラーハンドリング、フラッシュメッセージ、ページネーション |
1678
-
1679
- ### 技術スタック
1680
-
1681
- | カテゴリ | 技術 |
1682
- |---------|------|
1683
- | **言語** | Java 21 |
1684
- | **フレームワーク** | Spring Boot 3.4 |
1685
- | **テンプレート** | Thymeleaf |
1686
- | **部分更新** | htmx |
1687
- | **CSS** | Bootstrap 5 |
1688
- | **ORM** | MyBatis |
1689
- | **データベース** | PostgreSQL 16 |
1690
- | **Excel** | Apache POI |
1691
- | **PDF** | OpenHTMLtoPDF(Thymeleaf + HTML/CSS) |
1692
- | **テスト** | JUnit 5, TestContainers |
1693
-
1694
- ### モノリスを選択すべき場面
1695
-
1696
- 1. **社内システム**: 限られたユーザー、SEO 不要
1697
- 2. **業務アプリケーション**: 複雑な業務フロー
1698
- 3. **小規模チーム**: フルスタック開発
1699
- 4. **迅速な開発**: MVP、プロトタイプ
1700
- 5. **運用コスト重視**: シンプルなインフラ
1701
-
1702
- モノリスアーキテクチャは決して「古い」アーキテクチャではありません。適切な場面で選択することで、シンプルかつ効率的なシステム開発が可能です。