@k2works/claude-code-booster 6.2.0 → 6.3.1

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 (618) hide show
  1. package/lib/assets/.claude/skills/practicing-getting-start-tdd/SKILL.md +3 -7
  2. package/lib/assets/docs/article/index.md +17 -41
  3. package/lib/assets/docs/index.md +1 -1
  4. package/lib/assets/mkdocs.yml +0 -6
  5. package/package.json +1 -1
  6. package/lib/assets/docs/article/functional-desgin-ppp/all/01-immutability-and-data-transformation.md +0 -484
  7. package/lib/assets/docs/article/functional-desgin-ppp/all/02-function-composition.md +0 -528
  8. package/lib/assets/docs/article/functional-desgin-ppp/all/03-polymorphism.md +0 -546
  9. package/lib/assets/docs/article/functional-desgin-ppp/all/04-data-validation.md +0 -309
  10. package/lib/assets/docs/article/functional-desgin-ppp/all/05-property-based-testing.md +0 -329
  11. package/lib/assets/docs/article/functional-desgin-ppp/all/06-tdd-and-functional.md +0 -507
  12. package/lib/assets/docs/article/functional-desgin-ppp/all/07-composite-pattern.md +0 -307
  13. package/lib/assets/docs/article/functional-desgin-ppp/all/08-decorator-pattern.md +0 -300
  14. package/lib/assets/docs/article/functional-desgin-ppp/all/09-adapter-pattern.md +0 -345
  15. package/lib/assets/docs/article/functional-desgin-ppp/all/10-strategy-pattern.md +0 -312
  16. package/lib/assets/docs/article/functional-desgin-ppp/all/11-command-pattern.md +0 -295
  17. package/lib/assets/docs/article/functional-desgin-ppp/all/12-visitor-pattern.md +0 -331
  18. package/lib/assets/docs/article/functional-desgin-ppp/all/13-abstract-factory-pattern.md +0 -328
  19. package/lib/assets/docs/article/functional-desgin-ppp/all/14-abstract-server-pattern.md +0 -374
  20. package/lib/assets/docs/article/functional-desgin-ppp/all/15-gossiping-bus-drivers.md +0 -165
  21. package/lib/assets/docs/article/functional-desgin-ppp/all/16-payroll-system.md +0 -187
  22. package/lib/assets/docs/article/functional-desgin-ppp/all/17-video-rental-system.md +0 -321
  23. package/lib/assets/docs/article/functional-desgin-ppp/all/18-concurrency-system.md +0 -296
  24. package/lib/assets/docs/article/functional-desgin-ppp/all/19-wa-tor-simulation.md +0 -295
  25. package/lib/assets/docs/article/functional-desgin-ppp/all/20-pattern-interactions.md +0 -283
  26. package/lib/assets/docs/article/functional-desgin-ppp/all/21-best-practices.md +0 -303
  27. package/lib/assets/docs/article/functional-desgin-ppp/all/22-oo-to-fp-migration.md +0 -346
  28. package/lib/assets/docs/article/functional-desgin-ppp/all/index.md +0 -388
  29. package/lib/assets/docs/article/functional-desgin-ppp/clojure/01-immutability-and-data-transformation.md +0 -282
  30. package/lib/assets/docs/article/functional-desgin-ppp/clojure/02-function-composition.md +0 -389
  31. package/lib/assets/docs/article/functional-desgin-ppp/clojure/03-polymorphism.md +0 -393
  32. package/lib/assets/docs/article/functional-desgin-ppp/clojure/04-clojure-spec.md +0 -359
  33. package/lib/assets/docs/article/functional-desgin-ppp/clojure/05-property-based-testing.md +0 -361
  34. package/lib/assets/docs/article/functional-desgin-ppp/clojure/06-tdd-in-functional.md +0 -392
  35. package/lib/assets/docs/article/functional-desgin-ppp/clojure/07-composite-pattern.md +0 -538
  36. package/lib/assets/docs/article/functional-desgin-ppp/clojure/08-decorator-pattern.md +0 -404
  37. package/lib/assets/docs/article/functional-desgin-ppp/clojure/09-adapter-pattern.md +0 -408
  38. package/lib/assets/docs/article/functional-desgin-ppp/clojure/10-strategy-pattern.md +0 -494
  39. package/lib/assets/docs/article/functional-desgin-ppp/clojure/11-command-pattern.md +0 -575
  40. package/lib/assets/docs/article/functional-desgin-ppp/clojure/12-visitor-pattern.md +0 -576
  41. package/lib/assets/docs/article/functional-desgin-ppp/clojure/13-abstract-factory-pattern.md +0 -484
  42. package/lib/assets/docs/article/functional-desgin-ppp/clojure/14-abstract-server-pattern.md +0 -471
  43. package/lib/assets/docs/article/functional-desgin-ppp/clojure/15-gossiping-bus-drivers.md +0 -334
  44. package/lib/assets/docs/article/functional-desgin-ppp/clojure/16-payroll-system.md +0 -410
  45. package/lib/assets/docs/article/functional-desgin-ppp/clojure/17-video-rental-system.md +0 -459
  46. package/lib/assets/docs/article/functional-desgin-ppp/clojure/18-concurrency-system.md +0 -484
  47. package/lib/assets/docs/article/functional-desgin-ppp/clojure/19-wator-simulation.md +0 -748
  48. package/lib/assets/docs/article/functional-desgin-ppp/clojure/20-pattern-interactions.md +0 -576
  49. package/lib/assets/docs/article/functional-desgin-ppp/clojure/21-best-practices.md +0 -527
  50. package/lib/assets/docs/article/functional-desgin-ppp/clojure/22-oo-to-fp-migration.md +0 -541
  51. package/lib/assets/docs/article/functional-desgin-ppp/clojure/index.md +0 -241
  52. package/lib/assets/docs/article/functional-desgin-ppp/elixir/01-immutability-and-data-transformation.md +0 -392
  53. package/lib/assets/docs/article/functional-desgin-ppp/elixir/02-function-composition.md +0 -383
  54. package/lib/assets/docs/article/functional-desgin-ppp/elixir/03-polymorphism.md +0 -384
  55. package/lib/assets/docs/article/functional-desgin-ppp/elixir/04-data-validation.md +0 -204
  56. package/lib/assets/docs/article/functional-desgin-ppp/elixir/05-property-based-testing.md +0 -277
  57. package/lib/assets/docs/article/functional-desgin-ppp/elixir/06-tdd-and-fp.md +0 -303
  58. package/lib/assets/docs/article/functional-desgin-ppp/elixir/07-effects-and-pure-functions.md +0 -173
  59. package/lib/assets/docs/article/functional-desgin-ppp/elixir/08-error-handling-strategies.md +0 -177
  60. package/lib/assets/docs/article/functional-desgin-ppp/elixir/09-io-and-external-systems.md +0 -263
  61. package/lib/assets/docs/article/functional-desgin-ppp/elixir/10-concurrency-patterns.md +0 -278
  62. package/lib/assets/docs/article/functional-desgin-ppp/elixir/11-command-pattern.md +0 -157
  63. package/lib/assets/docs/article/functional-desgin-ppp/elixir/12-visitor-pattern.md +0 -185
  64. package/lib/assets/docs/article/functional-desgin-ppp/elixir/13-abstract-factory-pattern.md +0 -613
  65. package/lib/assets/docs/article/functional-desgin-ppp/elixir/14-abstract-server-pattern.md +0 -738
  66. package/lib/assets/docs/article/functional-desgin-ppp/elixir/15-gossiping-bus-drivers.md +0 -300
  67. package/lib/assets/docs/article/functional-desgin-ppp/elixir/16-payroll-system.md +0 -429
  68. package/lib/assets/docs/article/functional-desgin-ppp/elixir/17-video-rental-system.md +0 -328
  69. package/lib/assets/docs/article/functional-desgin-ppp/elixir/18-concurrency-system.md +0 -475
  70. package/lib/assets/docs/article/functional-desgin-ppp/elixir/19-wator-simulation.md +0 -532
  71. package/lib/assets/docs/article/functional-desgin-ppp/elixir/20-pattern-interactions.md +0 -296
  72. package/lib/assets/docs/article/functional-desgin-ppp/elixir/21-best-practices.md +0 -349
  73. package/lib/assets/docs/article/functional-desgin-ppp/elixir/22-oo-to-fp-migration.md +0 -404
  74. package/lib/assets/docs/article/functional-desgin-ppp/elixir/index.md +0 -248
  75. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/01-immutability-and-data-transformation.md +0 -393
  76. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/02-function-composition.md +0 -461
  77. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/03-polymorphism.md +0 -504
  78. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/04-data-validation.md +0 -425
  79. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/05-property-based-testing.md +0 -391
  80. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/06-tdd-functional.md +0 -696
  81. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/07-composite-pattern.md +0 -451
  82. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/08-decorator-pattern.md +0 -488
  83. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/09-adapter-pattern.md +0 -488
  84. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/10-strategy-pattern.md +0 -436
  85. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/11-command-pattern.md +0 -437
  86. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/12-visitor-pattern.md +0 -348
  87. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/13-abstract-factory-pattern.md +0 -318
  88. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/14-abstract-server-pattern.md +0 -605
  89. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/15-gossiping-bus-drivers.md +0 -364
  90. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/16-payroll-system.md +0 -359
  91. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/17-video-rental-system.md +0 -423
  92. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/18-concurrency-system.md +0 -376
  93. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/19-wator-simulation.md +0 -412
  94. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/20-pattern-interactions.md +0 -300
  95. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/21-best-practices.md +0 -333
  96. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/22-oo-to-fp-migration.md +0 -341
  97. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/index.md +0 -274
  98. package/lib/assets/docs/article/functional-desgin-ppp/haskell/01-immutability-and-data-transformation.md +0 -307
  99. package/lib/assets/docs/article/functional-desgin-ppp/haskell/02-function-composition.md +0 -313
  100. package/lib/assets/docs/article/functional-desgin-ppp/haskell/03-polymorphism.md +0 -371
  101. package/lib/assets/docs/article/functional-desgin-ppp/haskell/04-data-validation.md +0 -266
  102. package/lib/assets/docs/article/functional-desgin-ppp/haskell/05-property-based-testing.md +0 -263
  103. package/lib/assets/docs/article/functional-desgin-ppp/haskell/06-tdd-functional.md +0 -292
  104. package/lib/assets/docs/article/functional-desgin-ppp/haskell/07-composite-pattern.md +0 -404
  105. package/lib/assets/docs/article/functional-desgin-ppp/haskell/08-decorator-pattern.md +0 -328
  106. package/lib/assets/docs/article/functional-desgin-ppp/haskell/09-adapter-pattern.md +0 -391
  107. package/lib/assets/docs/article/functional-desgin-ppp/haskell/10-strategy-pattern.md +0 -296
  108. package/lib/assets/docs/article/functional-desgin-ppp/haskell/11-command-pattern.md +0 -312
  109. package/lib/assets/docs/article/functional-desgin-ppp/haskell/12-visitor-pattern.md +0 -335
  110. package/lib/assets/docs/article/functional-desgin-ppp/haskell/13-abstract-factory-pattern.md +0 -341
  111. package/lib/assets/docs/article/functional-desgin-ppp/haskell/14-abstract-server-pattern.md +0 -388
  112. package/lib/assets/docs/article/functional-desgin-ppp/haskell/15-gossiping-bus-drivers.md +0 -186
  113. package/lib/assets/docs/article/functional-desgin-ppp/haskell/16-payroll-system.md +0 -228
  114. package/lib/assets/docs/article/functional-desgin-ppp/haskell/17-video-rental-system.md +0 -253
  115. package/lib/assets/docs/article/functional-desgin-ppp/haskell/18-concurrency-system.md +0 -372
  116. package/lib/assets/docs/article/functional-desgin-ppp/haskell/19-wator-simulation.md +0 -447
  117. package/lib/assets/docs/article/functional-desgin-ppp/haskell/20-pattern-interactions.md +0 -334
  118. package/lib/assets/docs/article/functional-desgin-ppp/haskell/21-best-practices.md +0 -412
  119. package/lib/assets/docs/article/functional-desgin-ppp/haskell/22-oo-to-fp-migration.md +0 -478
  120. package/lib/assets/docs/article/functional-desgin-ppp/haskell/index.md +0 -174
  121. package/lib/assets/docs/article/functional-desgin-ppp/index.md +0 -90
  122. package/lib/assets/docs/article/functional-desgin-ppp/rust/01-immutability-and-data-transformation.md +0 -459
  123. package/lib/assets/docs/article/functional-desgin-ppp/rust/02-function-composition.md +0 -472
  124. package/lib/assets/docs/article/functional-desgin-ppp/rust/03-polymorphism.md +0 -434
  125. package/lib/assets/docs/article/functional-desgin-ppp/rust/04-data-validation.md +0 -282
  126. package/lib/assets/docs/article/functional-desgin-ppp/rust/05-property-based-testing.md +0 -256
  127. package/lib/assets/docs/article/functional-desgin-ppp/rust/06-tdd-and-functional.md +0 -850
  128. package/lib/assets/docs/article/functional-desgin-ppp/rust/07-composite-pattern.md +0 -393
  129. package/lib/assets/docs/article/functional-desgin-ppp/rust/08-decorator-pattern.md +0 -392
  130. package/lib/assets/docs/article/functional-desgin-ppp/rust/09-adapter-pattern.md +0 -348
  131. package/lib/assets/docs/article/functional-desgin-ppp/rust/10-strategy-pattern.md +0 -340
  132. package/lib/assets/docs/article/functional-desgin-ppp/rust/11-command-pattern.md +0 -365
  133. package/lib/assets/docs/article/functional-desgin-ppp/rust/12-visitor-pattern.md +0 -388
  134. package/lib/assets/docs/article/functional-desgin-ppp/rust/13-abstract-factory-pattern.md +0 -370
  135. package/lib/assets/docs/article/functional-desgin-ppp/rust/14-abstract-server-pattern.md +0 -401
  136. package/lib/assets/docs/article/functional-desgin-ppp/rust/15-gossiping-bus-drivers.md +0 -309
  137. package/lib/assets/docs/article/functional-desgin-ppp/rust/16-payroll-system.md +0 -306
  138. package/lib/assets/docs/article/functional-desgin-ppp/rust/17-video-rental-system.md +0 -313
  139. package/lib/assets/docs/article/functional-desgin-ppp/rust/18-concurrency-system.md +0 -324
  140. package/lib/assets/docs/article/functional-desgin-ppp/rust/19-wator-simulation.md +0 -320
  141. package/lib/assets/docs/article/functional-desgin-ppp/rust/20-pattern-interactions.md +0 -313
  142. package/lib/assets/docs/article/functional-desgin-ppp/rust/21-best-practices.md +0 -345
  143. package/lib/assets/docs/article/functional-desgin-ppp/rust/22-oo-to-fp-migration.md +0 -358
  144. package/lib/assets/docs/article/functional-desgin-ppp/rust/index.md +0 -243
  145. package/lib/assets/docs/article/functional-desgin-ppp/scala/01-immutability-and-data-transformation.md +0 -337
  146. package/lib/assets/docs/article/functional-desgin-ppp/scala/02-function-composition.md +0 -357
  147. package/lib/assets/docs/article/functional-desgin-ppp/scala/03-polymorphism.md +0 -366
  148. package/lib/assets/docs/article/functional-desgin-ppp/scala/04-data-validation.md +0 -373
  149. package/lib/assets/docs/article/functional-desgin-ppp/scala/05-property-based-testing.md +0 -524
  150. package/lib/assets/docs/article/functional-desgin-ppp/scala/06-tdd-functional.md +0 -566
  151. package/lib/assets/docs/article/functional-desgin-ppp/scala/07-composite-pattern.md +0 -372
  152. package/lib/assets/docs/article/functional-desgin-ppp/scala/08-decorator-pattern.md +0 -336
  153. package/lib/assets/docs/article/functional-desgin-ppp/scala/09-adapter-pattern.md +0 -526
  154. package/lib/assets/docs/article/functional-desgin-ppp/scala/10-strategy-pattern.md +0 -450
  155. package/lib/assets/docs/article/functional-desgin-ppp/scala/11-command-pattern.md +0 -416
  156. package/lib/assets/docs/article/functional-desgin-ppp/scala/12-visitor-pattern.md +0 -388
  157. package/lib/assets/docs/article/functional-desgin-ppp/scala/13-abstract-factory-pattern.md +0 -407
  158. package/lib/assets/docs/article/functional-desgin-ppp/scala/14-abstract-server-pattern.md +0 -485
  159. package/lib/assets/docs/article/functional-desgin-ppp/scala/15-gossiping-bus-drivers.md +0 -400
  160. package/lib/assets/docs/article/functional-desgin-ppp/scala/16-payroll-system.md +0 -351
  161. package/lib/assets/docs/article/functional-desgin-ppp/scala/17-video-rental-system.md +0 -333
  162. package/lib/assets/docs/article/functional-desgin-ppp/scala/18-concurrency-system.md +0 -739
  163. package/lib/assets/docs/article/functional-desgin-ppp/scala/19-wator-simulation.md +0 -633
  164. package/lib/assets/docs/article/functional-desgin-ppp/scala/20-pattern-interactions.md +0 -521
  165. package/lib/assets/docs/article/functional-desgin-ppp/scala/21-best-practices.md +0 -442
  166. package/lib/assets/docs/article/functional-desgin-ppp/scala/22-oo-to-fp-migration.md +0 -697
  167. package/lib/assets/docs/article/functional-desgin-ppp/scala/index.md +0 -243
  168. package/lib/assets/docs/article/getting-start-docker-kubernetes/01-container-and-docker-basics.md +0 -341
  169. package/lib/assets/docs/article/getting-start-docker-kubernetes/02-container-deployment.md +0 -785
  170. package/lib/assets/docs/article/getting-start-docker-kubernetes/03-practical-container-build-deploy.md +0 -466
  171. package/lib/assets/docs/article/getting-start-docker-kubernetes/04-multi-container-application.md +0 -878
  172. package/lib/assets/docs/article/getting-start-docker-kubernetes/05-kubernetes-introduction.md +0 -585
  173. package/lib/assets/docs/article/getting-start-docker-kubernetes/06-kubernetes-deploy-cluster.md +0 -620
  174. package/lib/assets/docs/article/getting-start-docker-kubernetes/07-kubernetes-advanced.md +0 -621
  175. package/lib/assets/docs/article/getting-start-docker-kubernetes/08-kubernetes-packaging.md +0 -518
  176. package/lib/assets/docs/article/getting-start-docker-kubernetes/09-container-operations.md +0 -546
  177. package/lib/assets/docs/article/getting-start-docker-kubernetes/10-optimal-container-image.md +0 -640
  178. package/lib/assets/docs/article/getting-start-docker-kubernetes/11-continuous-delivery.md +0 -607
  179. package/lib/assets/docs/article/getting-start-docker-kubernetes/12-container-use-cases.md +0 -463
  180. package/lib/assets/docs/article/getting-start-docker-kubernetes/13-case-monolith-compose-vs-kustomize.md +0 -346
  181. package/lib/assets/docs/article/getting-start-docker-kubernetes/14-case-event-driven-kustomize-vs-helm.md +0 -324
  182. package/lib/assets/docs/article/getting-start-docker-kubernetes/15-case-escqrs-axon-kustomize-vs-helm.md +0 -297
  183. package/lib/assets/docs/article/getting-start-docker-kubernetes/16-case-escqrs-kafka-kustomize-vs-helm.md +0 -256
  184. package/lib/assets/docs/article/getting-start-docker-kubernetes/17-case-comparison-summary.md +0 -141
  185. package/lib/assets/docs/article/getting-start-docker-kubernetes/18-case-load-testing.md +0 -303
  186. package/lib/assets/docs/article/getting-start-docker-kubernetes/appendix-a-dev-tools-setup.md +0 -274
  187. package/lib/assets/docs/article/getting-start-docker-kubernetes/appendix-b-orchestration-environments.md +0 -530
  188. package/lib/assets/docs/article/getting-start-docker-kubernetes/appendix-c-tips.md +0 -519
  189. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-01-comic.png +0 -0
  190. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-02-comic.png +0 -0
  191. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-03-comic.png +0 -0
  192. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-04-comic.png +0 -0
  193. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-05-comic.png +0 -0
  194. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-06-comic.png +0 -0
  195. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-07-comic.png +0 -0
  196. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-08-comic.png +0 -0
  197. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-09-comic.png +0 -0
  198. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-10-comic.png +0 -0
  199. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-11-comic.png +0 -0
  200. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-12-comic.png +0 -0
  201. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/overview-comic.png +0 -0
  202. package/lib/assets/docs/article/getting-start-docker-kubernetes/index.md +0 -82
  203. package/lib/assets/docs/article/getting-start-docker-kubernetes/outline.md +0 -220
  204. package/lib/assets/docs/article/getting-start-tdd/clojure/01-todo-list-and-first-test.md +0 -175
  205. package/lib/assets/docs/article/getting-start-tdd/clojure/02-fake-it-and-triangulation.md +0 -171
  206. package/lib/assets/docs/article/getting-start-tdd/clojure/03-obvious-implementation-and-refactoring.md +0 -144
  207. package/lib/assets/docs/article/getting-start-tdd/clojure/04-version-control-and-conventional-commits.md +0 -97
  208. package/lib/assets/docs/article/getting-start-tdd/clojure/05-package-management-and-static-analysis.md +0 -308
  209. package/lib/assets/docs/article/getting-start-tdd/clojure/06-task-runner-and-ci-cd.md +0 -250
  210. package/lib/assets/docs/article/getting-start-tdd/clojure/07-protocols-and-records.md +0 -140
  211. package/lib/assets/docs/article/getting-start-tdd/clojure/08-multimethods-and-design-patterns.md +0 -139
  212. package/lib/assets/docs/article/getting-start-tdd/clojure/09-namespaces-and-module-design.md +0 -136
  213. package/lib/assets/docs/article/getting-start-tdd/clojure/10-higher-order-functions-and-composition.md +0 -123
  214. package/lib/assets/docs/article/getting-start-tdd/clojure/11-persistent-data-and-pipeline.md +0 -147
  215. package/lib/assets/docs/article/getting-start-tdd/clojure/12-error-handling-and-spec.md +0 -170
  216. package/lib/assets/docs/article/getting-start-tdd/clojure/index.md +0 -65
  217. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter01.md +0 -241
  218. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter02.md +0 -253
  219. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter03.md +0 -211
  220. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter04.md +0 -101
  221. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter05.md +0 -265
  222. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter06.md +0 -204
  223. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter07.md +0 -223
  224. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter08.md +0 -258
  225. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter09.md +0 -183
  226. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter10.md +0 -175
  227. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter11.md +0 -201
  228. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter12.md +0 -220
  229. package/lib/assets/docs/article/getting-start-tdd/csharp/index.md +0 -83
  230. package/lib/assets/docs/article/getting-start-tdd/elixir/01-todo-list-and-first-test.md +0 -96
  231. package/lib/assets/docs/article/getting-start-tdd/elixir/02-fake-it-and-triangulation.md +0 -104
  232. package/lib/assets/docs/article/getting-start-tdd/elixir/03-obvious-implementation-and-refactoring.md +0 -118
  233. package/lib/assets/docs/article/getting-start-tdd/elixir/04-version-control-and-conventional-commits.md +0 -105
  234. package/lib/assets/docs/article/getting-start-tdd/elixir/05-package-management-and-static-analysis.md +0 -97
  235. package/lib/assets/docs/article/getting-start-tdd/elixir/06-task-runner-and-ci-cd.md +0 -80
  236. package/lib/assets/docs/article/getting-start-tdd/elixir/07-structs-and-protocols.md +0 -119
  237. package/lib/assets/docs/article/getting-start-tdd/elixir/08-pattern-matching-and-guards.md +0 -117
  238. package/lib/assets/docs/article/getting-start-tdd/elixir/09-module-design-and-behaviours.md +0 -113
  239. package/lib/assets/docs/article/getting-start-tdd/elixir/10-higher-order-functions-and-pipeline.md +0 -187
  240. package/lib/assets/docs/article/getting-start-tdd/elixir/11-stream-and-lazy-evaluation.md +0 -151
  241. package/lib/assets/docs/article/getting-start-tdd/elixir/12-error-handling-and-with.md +0 -154
  242. package/lib/assets/docs/article/getting-start-tdd/elixir/index.md +0 -35
  243. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter01.md +0 -211
  244. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter02.md +0 -255
  245. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter03.md +0 -227
  246. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter04.md +0 -188
  247. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter05.md +0 -276
  248. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter06.md +0 -199
  249. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter07.md +0 -170
  250. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter08.md +0 -184
  251. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter09.md +0 -231
  252. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter10.md +0 -198
  253. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter11.md +0 -221
  254. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter12.md +0 -224
  255. package/lib/assets/docs/article/getting-start-tdd/fsharp/index.md +0 -71
  256. package/lib/assets/docs/article/getting-start-tdd/go/01-todo-list-and-first-test.md +0 -222
  257. package/lib/assets/docs/article/getting-start-tdd/go/02-fake-it-and-triangulation.md +0 -311
  258. package/lib/assets/docs/article/getting-start-tdd/go/03-obvious-implementation-and-refactoring.md +0 -348
  259. package/lib/assets/docs/article/getting-start-tdd/go/04-version-control-and-conventional-commits.md +0 -121
  260. package/lib/assets/docs/article/getting-start-tdd/go/05-package-management-and-static-analysis.md +0 -281
  261. package/lib/assets/docs/article/getting-start-tdd/go/06-task-runner-and-ci-cd.md +0 -242
  262. package/lib/assets/docs/article/getting-start-tdd/go/07-encapsulation-and-polymorphism.md +0 -402
  263. package/lib/assets/docs/article/getting-start-tdd/go/08-design-patterns.md +0 -430
  264. package/lib/assets/docs/article/getting-start-tdd/go/09-solid-principles-and-module-design.md +0 -408
  265. package/lib/assets/docs/article/getting-start-tdd/go/10-higher-order-functions-and-composition.md +0 -234
  266. package/lib/assets/docs/article/getting-start-tdd/go/11-immutable-data-and-pipeline.md +0 -304
  267. package/lib/assets/docs/article/getting-start-tdd/go/12-error-handling-and-type-safety.md +0 -419
  268. package/lib/assets/docs/article/getting-start-tdd/go/index.md +0 -83
  269. package/lib/assets/docs/article/getting-start-tdd/haskell/01-todo-list-and-first-test.md +0 -288
  270. package/lib/assets/docs/article/getting-start-tdd/haskell/02-fake-it-and-triangulation.md +0 -346
  271. package/lib/assets/docs/article/getting-start-tdd/haskell/03-obvious-implementation-and-refactoring.md +0 -266
  272. package/lib/assets/docs/article/getting-start-tdd/haskell/04-version-control-and-conventional-commits.md +0 -191
  273. package/lib/assets/docs/article/getting-start-tdd/haskell/05-package-management-and-static-analysis.md +0 -322
  274. package/lib/assets/docs/article/getting-start-tdd/haskell/06-task-runner-and-ci-cd.md +0 -318
  275. package/lib/assets/docs/article/getting-start-tdd/haskell/07-algebraic-data-types-and-type-classes.md +0 -421
  276. package/lib/assets/docs/article/getting-start-tdd/haskell/08-pattern-matching-and-guards.md +0 -399
  277. package/lib/assets/docs/article/getting-start-tdd/haskell/09-module-design-and-smart-constructors.md +0 -470
  278. package/lib/assets/docs/article/getting-start-tdd/haskell/10-higher-order-functions-and-currying.md +0 -443
  279. package/lib/assets/docs/article/getting-start-tdd/haskell/11-function-composition-and-point-free.md +0 -401
  280. package/lib/assets/docs/article/getting-start-tdd/haskell/12-monad-and-error-handling.md +0 -640
  281. package/lib/assets/docs/article/getting-start-tdd/haskell/index.md +0 -49
  282. package/lib/assets/docs/article/getting-start-tdd/index.md +0 -93
  283. package/lib/assets/docs/article/getting-start-tdd/integration/01-language-overview.md +0 -384
  284. package/lib/assets/docs/article/getting-start-tdd/integration/02-test-framework-comparison.md +0 -358
  285. package/lib/assets/docs/article/getting-start-tdd/integration/03-tdd-pattern-comparison.md +0 -454
  286. package/lib/assets/docs/article/getting-start-tdd/integration/04-type-system-comparison.md +0 -418
  287. package/lib/assets/docs/article/getting-start-tdd/integration/05-dev-environment-comparison.md +0 -339
  288. package/lib/assets/docs/article/getting-start-tdd/integration/06-learning-roadmap.md +0 -299
  289. package/lib/assets/docs/article/getting-start-tdd/integration/index.md +0 -69
  290. package/lib/assets/docs/article/getting-start-tdd/java/01-todo-list-and-first-test.md +0 -243
  291. package/lib/assets/docs/article/getting-start-tdd/java/02-fake-it-and-triangulation.md +0 -270
  292. package/lib/assets/docs/article/getting-start-tdd/java/03-obvious-implementation-and-refactoring.md +0 -194
  293. package/lib/assets/docs/article/getting-start-tdd/java/04-version-control-and-conventional-commits.md +0 -124
  294. package/lib/assets/docs/article/getting-start-tdd/java/05-package-management-and-static-analysis.md +0 -391
  295. package/lib/assets/docs/article/getting-start-tdd/java/06-task-runner-and-ci-cd.md +0 -281
  296. package/lib/assets/docs/article/getting-start-tdd/java/07-encapsulation-and-polymorphism.md +0 -635
  297. package/lib/assets/docs/article/getting-start-tdd/java/08-design-patterns.md +0 -402
  298. package/lib/assets/docs/article/getting-start-tdd/java/09-solid-principles-and-module-design.md +0 -319
  299. package/lib/assets/docs/article/getting-start-tdd/java/10-higher-order-functions-and-composition.md +0 -197
  300. package/lib/assets/docs/article/getting-start-tdd/java/11-immutable-data-and-pipeline.md +0 -176
  301. package/lib/assets/docs/article/getting-start-tdd/java/12-error-handling-and-type-safety.md +0 -214
  302. package/lib/assets/docs/article/getting-start-tdd/java/index.md +0 -61
  303. package/lib/assets/docs/article/getting-start-tdd/node/01-todo-list-and-first-test.md +0 -253
  304. package/lib/assets/docs/article/getting-start-tdd/node/02-fake-it-and-triangulation.md +0 -271
  305. package/lib/assets/docs/article/getting-start-tdd/node/03-obvious-implementation-and-refactoring.md +0 -178
  306. package/lib/assets/docs/article/getting-start-tdd/node/04-version-control-and-conventional-commits.md +0 -121
  307. package/lib/assets/docs/article/getting-start-tdd/node/05-package-management-and-static-analysis.md +0 -323
  308. package/lib/assets/docs/article/getting-start-tdd/node/06-task-runner-and-ci-cd.md +0 -244
  309. package/lib/assets/docs/article/getting-start-tdd/node/07-encapsulation-and-polymorphism.md +0 -336
  310. package/lib/assets/docs/article/getting-start-tdd/node/08-design-patterns.md +0 -331
  311. package/lib/assets/docs/article/getting-start-tdd/node/09-solid-principles-and-module-design.md +0 -294
  312. package/lib/assets/docs/article/getting-start-tdd/node/10-higher-order-functions-and-composition.md +0 -208
  313. package/lib/assets/docs/article/getting-start-tdd/node/11-immutable-data-and-pipeline.md +0 -216
  314. package/lib/assets/docs/article/getting-start-tdd/node/12-error-handling-and-type-safety.md +0 -304
  315. package/lib/assets/docs/article/getting-start-tdd/node/index.md +0 -56
  316. package/lib/assets/docs/article/getting-start-tdd/php/01-todo-list-and-first-test.md +0 -268
  317. package/lib/assets/docs/article/getting-start-tdd/php/02-fake-it-and-triangulation.md +0 -209
  318. package/lib/assets/docs/article/getting-start-tdd/php/03-obvious-implementation-and-refactoring.md +0 -257
  319. package/lib/assets/docs/article/getting-start-tdd/php/04-version-control-and-conventional-commits.md +0 -150
  320. package/lib/assets/docs/article/getting-start-tdd/php/05-package-management-and-static-analysis.md +0 -419
  321. package/lib/assets/docs/article/getting-start-tdd/php/06-task-runner-and-ci-cd.md +0 -330
  322. package/lib/assets/docs/article/getting-start-tdd/php/07-encapsulation-and-polymorphism.md +0 -380
  323. package/lib/assets/docs/article/getting-start-tdd/php/08-design-patterns.md +0 -461
  324. package/lib/assets/docs/article/getting-start-tdd/php/09-solid-principles-and-module-design.md +0 -468
  325. package/lib/assets/docs/article/getting-start-tdd/php/10-higher-order-functions-and-composition.md +0 -190
  326. package/lib/assets/docs/article/getting-start-tdd/php/11-immutable-data-and-pipeline.md +0 -274
  327. package/lib/assets/docs/article/getting-start-tdd/php/12-error-handling-and-type-safety.md +0 -316
  328. package/lib/assets/docs/article/getting-start-tdd/php/index.md +0 -84
  329. package/lib/assets/docs/article/getting-start-tdd/python/01-todo-list-and-first-test.md +0 -210
  330. package/lib/assets/docs/article/getting-start-tdd/python/02-fake-it-and-triangulation.md +0 -256
  331. package/lib/assets/docs/article/getting-start-tdd/python/03-obvious-implementation-and-refactoring.md +0 -208
  332. package/lib/assets/docs/article/getting-start-tdd/python/04-version-control-and-conventional-commits.md +0 -96
  333. package/lib/assets/docs/article/getting-start-tdd/python/05-package-management-and-static-analysis.md +0 -283
  334. package/lib/assets/docs/article/getting-start-tdd/python/06-task-runner-and-ci-cd.md +0 -199
  335. package/lib/assets/docs/article/getting-start-tdd/python/07-encapsulation-and-polymorphism.md +0 -217
  336. package/lib/assets/docs/article/getting-start-tdd/python/08-design-patterns.md +0 -181
  337. package/lib/assets/docs/article/getting-start-tdd/python/09-solid-principles-and-module-design.md +0 -139
  338. package/lib/assets/docs/article/getting-start-tdd/python/10-higher-order-functions-and-composition.md +0 -131
  339. package/lib/assets/docs/article/getting-start-tdd/python/11-immutable-data-and-pipeline.md +0 -125
  340. package/lib/assets/docs/article/getting-start-tdd/python/12-error-handling-and-type-safety.md +0 -135
  341. package/lib/assets/docs/article/getting-start-tdd/python/index.md +0 -55
  342. package/lib/assets/docs/article/getting-start-tdd/ruby/01-todo-list-and-first-test.md +0 -240
  343. package/lib/assets/docs/article/getting-start-tdd/ruby/02-fake-it-and-triangulation.md +0 -247
  344. package/lib/assets/docs/article/getting-start-tdd/ruby/03-obvious-implementation-and-refactoring.md +0 -237
  345. package/lib/assets/docs/article/getting-start-tdd/ruby/04-version-control-and-conventional-commits.md +0 -121
  346. package/lib/assets/docs/article/getting-start-tdd/ruby/05-package-management-and-static-analysis.md +0 -296
  347. package/lib/assets/docs/article/getting-start-tdd/ruby/06-task-runner-and-ci-cd.md +0 -257
  348. package/lib/assets/docs/article/getting-start-tdd/ruby/07-encapsulation-and-polymorphism.md +0 -288
  349. package/lib/assets/docs/article/getting-start-tdd/ruby/08-design-patterns.md +0 -338
  350. package/lib/assets/docs/article/getting-start-tdd/ruby/09-solid-principles-and-module-design.md +0 -205
  351. package/lib/assets/docs/article/getting-start-tdd/ruby/10-higher-order-functions-and-composition.md +0 -184
  352. package/lib/assets/docs/article/getting-start-tdd/ruby/11-immutable-data-and-pipeline.md +0 -246
  353. package/lib/assets/docs/article/getting-start-tdd/ruby/12-error-handling-and-type-safety.md +0 -407
  354. package/lib/assets/docs/article/getting-start-tdd/ruby/index.md +0 -83
  355. package/lib/assets/docs/article/getting-start-tdd/rust/01-todo-list-and-first-test.md +0 -220
  356. package/lib/assets/docs/article/getting-start-tdd/rust/02-fake-it-and-triangulation.md +0 -273
  357. package/lib/assets/docs/article/getting-start-tdd/rust/03-obvious-implementation-and-refactoring.md +0 -242
  358. package/lib/assets/docs/article/getting-start-tdd/rust/04-version-control-and-conventional-commits.md +0 -101
  359. package/lib/assets/docs/article/getting-start-tdd/rust/05-package-management-and-static-analysis.md +0 -221
  360. package/lib/assets/docs/article/getting-start-tdd/rust/06-task-runner-and-ci-cd.md +0 -173
  361. package/lib/assets/docs/article/getting-start-tdd/rust/07-encapsulation-and-polymorphism.md +0 -151
  362. package/lib/assets/docs/article/getting-start-tdd/rust/08-design-patterns.md +0 -154
  363. package/lib/assets/docs/article/getting-start-tdd/rust/09-solid-principles-and-module-design.md +0 -119
  364. package/lib/assets/docs/article/getting-start-tdd/rust/10-higher-order-functions-and-composition.md +0 -103
  365. package/lib/assets/docs/article/getting-start-tdd/rust/11-immutable-data-and-pipeline.md +0 -114
  366. package/lib/assets/docs/article/getting-start-tdd/rust/12-error-handling-and-type-safety.md +0 -121
  367. package/lib/assets/docs/article/getting-start-tdd/rust/index.md +0 -83
  368. package/lib/assets/docs/article/getting-start-tdd/scala/01-todo-list-and-first-test.md +0 -120
  369. package/lib/assets/docs/article/getting-start-tdd/scala/02-fake-it-and-triangulation.md +0 -116
  370. package/lib/assets/docs/article/getting-start-tdd/scala/03-obvious-implementation-and-refactoring.md +0 -108
  371. package/lib/assets/docs/article/getting-start-tdd/scala/04-version-control-and-conventional-commits.md +0 -132
  372. package/lib/assets/docs/article/getting-start-tdd/scala/05-package-management-and-static-analysis.md +0 -205
  373. package/lib/assets/docs/article/getting-start-tdd/scala/06-task-runner-and-ci-cd.md +0 -195
  374. package/lib/assets/docs/article/getting-start-tdd/scala/07-case-classes-and-traits.md +0 -148
  375. package/lib/assets/docs/article/getting-start-tdd/scala/08-pattern-matching-and-sealed-traits.md +0 -115
  376. package/lib/assets/docs/article/getting-start-tdd/scala/09-packages-and-module-design.md +0 -84
  377. package/lib/assets/docs/article/getting-start-tdd/scala/10-higher-order-functions-and-composition.md +0 -113
  378. package/lib/assets/docs/article/getting-start-tdd/scala/11-collections-and-lazy-evaluation.md +0 -103
  379. package/lib/assets/docs/article/getting-start-tdd/scala/12-error-handling-and-type-safety.md +0 -101
  380. package/lib/assets/docs/article/getting-start-tdd/scala/index.md +0 -65
  381. package/lib/assets/docs/article/grokking-concurrency/all/index.md +0 -403
  382. package/lib/assets/docs/article/grokking-concurrency/all/part-1-ch02-sequential.md +0 -563
  383. package/lib/assets/docs/article/grokking-concurrency/all/part-2-ch04-05-threads.md +0 -478
  384. package/lib/assets/docs/article/grokking-concurrency/all/part-3-ch06-multitasking.md +0 -529
  385. package/lib/assets/docs/article/grokking-concurrency/all/part-4-ch07-parallel-patterns.md +0 -429
  386. package/lib/assets/docs/article/grokking-concurrency/all/part-5-ch08-09-synchronization.md +0 -519
  387. package/lib/assets/docs/article/grokking-concurrency/all/part-6-ch10-11-nonblocking-io.md +0 -444
  388. package/lib/assets/docs/article/grokking-concurrency/all/part-7-ch12-async.md +0 -474
  389. package/lib/assets/docs/article/grokking-concurrency/all/part-8-ch13-mapreduce.md +0 -386
  390. package/lib/assets/docs/article/grokking-concurrency/clojure/index.md +0 -106
  391. package/lib/assets/docs/article/grokking-concurrency/clojure/part-1.md +0 -107
  392. package/lib/assets/docs/article/grokking-concurrency/clojure/part-2.md +0 -100
  393. package/lib/assets/docs/article/grokking-concurrency/clojure/part-3.md +0 -121
  394. package/lib/assets/docs/article/grokking-concurrency/clojure/part-4.md +0 -122
  395. package/lib/assets/docs/article/grokking-concurrency/clojure/part-5.md +0 -117
  396. package/lib/assets/docs/article/grokking-concurrency/clojure/part-6.md +0 -88
  397. package/lib/assets/docs/article/grokking-concurrency/clojure/part-7.md +0 -99
  398. package/lib/assets/docs/article/grokking-concurrency/clojure/part-8.md +0 -119
  399. package/lib/assets/docs/article/grokking-concurrency/csharp/index.md +0 -91
  400. package/lib/assets/docs/article/grokking-concurrency/csharp/part-1.md +0 -96
  401. package/lib/assets/docs/article/grokking-concurrency/csharp/part-2.md +0 -122
  402. package/lib/assets/docs/article/grokking-concurrency/csharp/part-3.md +0 -100
  403. package/lib/assets/docs/article/grokking-concurrency/csharp/part-4.md +0 -111
  404. package/lib/assets/docs/article/grokking-concurrency/csharp/part-5.md +0 -98
  405. package/lib/assets/docs/article/grokking-concurrency/csharp/part-6.md +0 -60
  406. package/lib/assets/docs/article/grokking-concurrency/csharp/part-7.md +0 -83
  407. package/lib/assets/docs/article/grokking-concurrency/csharp/part-8.md +0 -91
  408. package/lib/assets/docs/article/grokking-concurrency/fsharp/index.md +0 -55
  409. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-1.md +0 -79
  410. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-2.md +0 -102
  411. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-3.md +0 -93
  412. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-4.md +0 -109
  413. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-5.md +0 -103
  414. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-6.md +0 -92
  415. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-7.md +0 -120
  416. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-8.md +0 -106
  417. package/lib/assets/docs/article/grokking-concurrency/haskell/index.md +0 -238
  418. package/lib/assets/docs/article/grokking-concurrency/haskell/part-1.md +0 -95
  419. package/lib/assets/docs/article/grokking-concurrency/haskell/part-2.md +0 -95
  420. package/lib/assets/docs/article/grokking-concurrency/haskell/part-3.md +0 -90
  421. package/lib/assets/docs/article/grokking-concurrency/haskell/part-4.md +0 -105
  422. package/lib/assets/docs/article/grokking-concurrency/haskell/part-5.md +0 -98
  423. package/lib/assets/docs/article/grokking-concurrency/haskell/part-6.md +0 -94
  424. package/lib/assets/docs/article/grokking-concurrency/haskell/part-7.md +0 -110
  425. package/lib/assets/docs/article/grokking-concurrency/haskell/part-8.md +0 -117
  426. package/lib/assets/docs/article/grokking-concurrency/index.md +0 -66
  427. package/lib/assets/docs/article/grokking-concurrency/java/index.md +0 -92
  428. package/lib/assets/docs/article/grokking-concurrency/java/part-1.md +0 -307
  429. package/lib/assets/docs/article/grokking-concurrency/java/part-2.md +0 -333
  430. package/lib/assets/docs/article/grokking-concurrency/java/part-3.md +0 -220
  431. package/lib/assets/docs/article/grokking-concurrency/java/part-4.md +0 -212
  432. package/lib/assets/docs/article/grokking-concurrency/java/part-5.md +0 -111
  433. package/lib/assets/docs/article/grokking-concurrency/java/part-6.md +0 -68
  434. package/lib/assets/docs/article/grokking-concurrency/java/part-7.md +0 -100
  435. package/lib/assets/docs/article/grokking-concurrency/java/part-8.md +0 -100
  436. package/lib/assets/docs/article/grokking-concurrency/python/index.md +0 -303
  437. package/lib/assets/docs/article/grokking-concurrency/python/part-1.md +0 -238
  438. package/lib/assets/docs/article/grokking-concurrency/python/part-2.md +0 -417
  439. package/lib/assets/docs/article/grokking-concurrency/python/part-3.md +0 -226
  440. package/lib/assets/docs/article/grokking-concurrency/python/part-4.md +0 -298
  441. package/lib/assets/docs/article/grokking-concurrency/python/part-5.md +0 -314
  442. package/lib/assets/docs/article/grokking-concurrency/python/part-6.md +0 -296
  443. package/lib/assets/docs/article/grokking-concurrency/python/part-7.md +0 -313
  444. package/lib/assets/docs/article/grokking-concurrency/python/part-8.md +0 -359
  445. package/lib/assets/docs/article/grokking-concurrency/rust/index.md +0 -260
  446. package/lib/assets/docs/article/grokking-concurrency/rust/part-1.md +0 -107
  447. package/lib/assets/docs/article/grokking-concurrency/rust/part-2.md +0 -119
  448. package/lib/assets/docs/article/grokking-concurrency/rust/part-3.md +0 -125
  449. package/lib/assets/docs/article/grokking-concurrency/rust/part-4.md +0 -174
  450. package/lib/assets/docs/article/grokking-concurrency/rust/part-5.md +0 -157
  451. package/lib/assets/docs/article/grokking-concurrency/rust/part-6.md +0 -93
  452. package/lib/assets/docs/article/grokking-concurrency/rust/part-7.md +0 -132
  453. package/lib/assets/docs/article/grokking-concurrency/rust/part-8.md +0 -154
  454. package/lib/assets/docs/article/grokking-concurrency/scala/index.md +0 -59
  455. package/lib/assets/docs/article/grokking-concurrency/scala/part-1.md +0 -77
  456. package/lib/assets/docs/article/grokking-concurrency/scala/part-2.md +0 -111
  457. package/lib/assets/docs/article/grokking-concurrency/scala/part-3.md +0 -92
  458. package/lib/assets/docs/article/grokking-concurrency/scala/part-4.md +0 -109
  459. package/lib/assets/docs/article/grokking-concurrency/scala/part-5.md +0 -118
  460. package/lib/assets/docs/article/grokking-concurrency/scala/part-6.md +0 -82
  461. package/lib/assets/docs/article/grokking-concurrency/scala/part-7.md +0 -130
  462. package/lib/assets/docs/article/grokking-concurrency/scala/part-8.md +0 -128
  463. package/lib/assets/docs/article/grokkingfp/all/index.md +0 -368
  464. package/lib/assets/docs/article/grokkingfp/all/part-1-ch01-fp-introduction.md +0 -539
  465. package/lib/assets/docs/article/grokkingfp/all/part-1-ch02-pure-functions.md +0 -932
  466. package/lib/assets/docs/article/grokkingfp/all/part-2-ch03-immutable-data.md +0 -1137
  467. package/lib/assets/docs/article/grokkingfp/all/part-2-ch04-higher-order-functions.md +0 -1113
  468. package/lib/assets/docs/article/grokkingfp/all/part-2-ch05-flatmap.md +0 -1035
  469. package/lib/assets/docs/article/grokkingfp/all/part-3-ch06-option.md +0 -794
  470. package/lib/assets/docs/article/grokkingfp/all/part-3-ch07-either-adt.md +0 -880
  471. package/lib/assets/docs/article/grokkingfp/all/part-4-ch08-io-monad.md +0 -981
  472. package/lib/assets/docs/article/grokkingfp/all/part-4-ch09-streams.md +0 -935
  473. package/lib/assets/docs/article/grokkingfp/all/part-5-ch10-concurrency.md +0 -879
  474. package/lib/assets/docs/article/grokkingfp/all/part-6-ch11-application.md +0 -724
  475. package/lib/assets/docs/article/grokkingfp/all/part-6-ch12-testing.md +0 -635
  476. package/lib/assets/docs/article/grokkingfp/all/writing-plan.md +0 -721
  477. package/lib/assets/docs/article/grokkingfp/clojure/index.md +0 -276
  478. package/lib/assets/docs/article/grokkingfp/clojure/part-1.md +0 -676
  479. package/lib/assets/docs/article/grokkingfp/clojure/part-2.md +0 -652
  480. package/lib/assets/docs/article/grokkingfp/clojure/part-3.md +0 -629
  481. package/lib/assets/docs/article/grokkingfp/clojure/part-4.md +0 -706
  482. package/lib/assets/docs/article/grokkingfp/clojure/part-5.md +0 -760
  483. package/lib/assets/docs/article/grokkingfp/clojure/part-6.md +0 -730
  484. package/lib/assets/docs/article/grokkingfp/csharp/index.md +0 -246
  485. package/lib/assets/docs/article/grokkingfp/csharp/part-1.md +0 -820
  486. package/lib/assets/docs/article/grokkingfp/csharp/part-2.md +0 -980
  487. package/lib/assets/docs/article/grokkingfp/csharp/part-3.md +0 -990
  488. package/lib/assets/docs/article/grokkingfp/csharp/part-4.md +0 -958
  489. package/lib/assets/docs/article/grokkingfp/csharp/part-5.md +0 -956
  490. package/lib/assets/docs/article/grokkingfp/csharp/part-6.md +0 -748
  491. package/lib/assets/docs/article/grokkingfp/elixir/index.md +0 -203
  492. package/lib/assets/docs/article/grokkingfp/elixir/part-1.md +0 -721
  493. package/lib/assets/docs/article/grokkingfp/elixir/part-2.md +0 -847
  494. package/lib/assets/docs/article/grokkingfp/elixir/part-3.md +0 -994
  495. package/lib/assets/docs/article/grokkingfp/elixir/part-4.md +0 -983
  496. package/lib/assets/docs/article/grokkingfp/elixir/part-5.md +0 -1295
  497. package/lib/assets/docs/article/grokkingfp/elixir/part-6.md +0 -1058
  498. package/lib/assets/docs/article/grokkingfp/fsharp/index.md +0 -210
  499. package/lib/assets/docs/article/grokkingfp/fsharp/part-1.md +0 -723
  500. package/lib/assets/docs/article/grokkingfp/fsharp/part-2.md +0 -970
  501. package/lib/assets/docs/article/grokkingfp/fsharp/part-3.md +0 -981
  502. package/lib/assets/docs/article/grokkingfp/fsharp/part-4.md +0 -841
  503. package/lib/assets/docs/article/grokkingfp/fsharp/part-5.md +0 -920
  504. package/lib/assets/docs/article/grokkingfp/fsharp/part-6.md +0 -931
  505. package/lib/assets/docs/article/grokkingfp/haskell/index.md +0 -234
  506. package/lib/assets/docs/article/grokkingfp/haskell/part-1.md +0 -600
  507. package/lib/assets/docs/article/grokkingfp/haskell/part-2.md +0 -875
  508. package/lib/assets/docs/article/grokkingfp/haskell/part-3.md +0 -924
  509. package/lib/assets/docs/article/grokkingfp/haskell/part-4.md +0 -887
  510. package/lib/assets/docs/article/grokkingfp/haskell/part-5.md +0 -854
  511. package/lib/assets/docs/article/grokkingfp/haskell/part-6.md +0 -853
  512. package/lib/assets/docs/article/grokkingfp/index.md +0 -143
  513. package/lib/assets/docs/article/grokkingfp/java/index.md +0 -211
  514. package/lib/assets/docs/article/grokkingfp/java/part-1.md +0 -657
  515. package/lib/assets/docs/article/grokkingfp/java/part-2.md +0 -684
  516. package/lib/assets/docs/article/grokkingfp/java/part-3.md +0 -681
  517. package/lib/assets/docs/article/grokkingfp/java/part-4.md +0 -780
  518. package/lib/assets/docs/article/grokkingfp/java/part-5.md +0 -968
  519. package/lib/assets/docs/article/grokkingfp/java/part-6.md +0 -1337
  520. package/lib/assets/docs/article/grokkingfp/python/index.md +0 -258
  521. package/lib/assets/docs/article/grokkingfp/python/part-1.md +0 -452
  522. package/lib/assets/docs/article/grokkingfp/python/part-2.md +0 -967
  523. package/lib/assets/docs/article/grokkingfp/python/part-3.md +0 -1013
  524. package/lib/assets/docs/article/grokkingfp/python/part-4.md +0 -774
  525. package/lib/assets/docs/article/grokkingfp/python/part-5.md +0 -756
  526. package/lib/assets/docs/article/grokkingfp/python/part-6.md +0 -870
  527. package/lib/assets/docs/article/grokkingfp/ruby/index.md +0 -330
  528. package/lib/assets/docs/article/grokkingfp/ruby/part-1.md +0 -764
  529. package/lib/assets/docs/article/grokkingfp/ruby/part-2.md +0 -947
  530. package/lib/assets/docs/article/grokkingfp/ruby/part-3.md +0 -955
  531. package/lib/assets/docs/article/grokkingfp/ruby/part-4.md +0 -930
  532. package/lib/assets/docs/article/grokkingfp/ruby/part-5.md +0 -917
  533. package/lib/assets/docs/article/grokkingfp/ruby/part-6.md +0 -1421
  534. package/lib/assets/docs/article/grokkingfp/rust/index.md +0 -242
  535. package/lib/assets/docs/article/grokkingfp/rust/part-1.md +0 -643
  536. package/lib/assets/docs/article/grokkingfp/rust/part-2.md +0 -1069
  537. package/lib/assets/docs/article/grokkingfp/rust/part-3.md +0 -1003
  538. package/lib/assets/docs/article/grokkingfp/rust/part-4.md +0 -582
  539. package/lib/assets/docs/article/grokkingfp/rust/part-5.md +0 -714
  540. package/lib/assets/docs/article/grokkingfp/rust/part-6.md +0 -517
  541. package/lib/assets/docs/article/grokkingfp/scala/index.md +0 -171
  542. package/lib/assets/docs/article/grokkingfp/scala/part-1.md +0 -552
  543. package/lib/assets/docs/article/grokkingfp/scala/part-2.md +0 -955
  544. package/lib/assets/docs/article/grokkingfp/scala/part-3.md +0 -928
  545. package/lib/assets/docs/article/grokkingfp/scala/part-4.md +0 -751
  546. package/lib/assets/docs/article/grokkingfp/scala/part-5.md +0 -731
  547. package/lib/assets/docs/article/grokkingfp/scala/part-6.md +0 -876
  548. package/lib/assets/docs/article/grokkingfp/typescript/index.md +0 -273
  549. package/lib/assets/docs/article/grokkingfp/typescript/part-1.md +0 -570
  550. package/lib/assets/docs/article/grokkingfp/typescript/part-2.md +0 -1138
  551. package/lib/assets/docs/article/grokkingfp/typescript/part-3.md +0 -851
  552. package/lib/assets/docs/article/grokkingfp/typescript/part-4.md +0 -1096
  553. package/lib/assets/docs/article/grokkingfp/typescript/part-5.md +0 -726
  554. package/lib/assets/docs/article/grokkingfp/typescript/part-6.md +0 -991
  555. package/lib/assets/docs/article/practical-database-design/index.md +0 -121
  556. package/lib/assets/docs/article/practical-database-design/part1/chapter01.md +0 -297
  557. package/lib/assets/docs/article/practical-database-design/part1/chapter02.md +0 -527
  558. package/lib/assets/docs/article/practical-database-design/part1/chapter03.md +0 -566
  559. package/lib/assets/docs/article/practical-database-design/part2/chapter04.md +0 -933
  560. package/lib/assets/docs/article/practical-database-design/part2/chapter05.md +0 -1636
  561. package/lib/assets/docs/article/practical-database-design/part2/chapter06.md +0 -2725
  562. package/lib/assets/docs/article/practical-database-design/part2/chapter07.md +0 -2091
  563. package/lib/assets/docs/article/practical-database-design/part2/chapter08.md +0 -2114
  564. package/lib/assets/docs/article/practical-database-design/part2/chapter09.md +0 -2040
  565. package/lib/assets/docs/article/practical-database-design/part2/chapter10.md +0 -1396
  566. package/lib/assets/docs/article/practical-database-design/part2/chapter11.md +0 -1686
  567. package/lib/assets/docs/article/practical-database-design/part2/chapter12.md +0 -1426
  568. package/lib/assets/docs/article/practical-database-design/part2/chapter13.md +0 -1443
  569. package/lib/assets/docs/article/practical-database-design/part3/chapter14.md +0 -676
  570. package/lib/assets/docs/article/practical-database-design/part3/chapter15.md +0 -1634
  571. package/lib/assets/docs/article/practical-database-design/part3/chapter16.md +0 -1924
  572. package/lib/assets/docs/article/practical-database-design/part3/chapter17.md +0 -1717
  573. package/lib/assets/docs/article/practical-database-design/part3/chapter18.md +0 -2104
  574. package/lib/assets/docs/article/practical-database-design/part3/chapter19.md +0 -1132
  575. package/lib/assets/docs/article/practical-database-design/part3/chapter20.md +0 -1040
  576. package/lib/assets/docs/article/practical-database-design/part3/chapter21.md +0 -1391
  577. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter14-orm.md +0 -1000
  578. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter15-orm.md +0 -1309
  579. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter16-orm.md +0 -1175
  580. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter17-orm.md +0 -1593
  581. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter18-orm.md +0 -1192
  582. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter19-orm.md +0 -1025
  583. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter20-orm.md +0 -1762
  584. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter21-orm.md +0 -1456
  585. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter22-orm.md +0 -1887
  586. package/lib/assets/docs/article/practical-database-design/part4/chapter22.md +0 -974
  587. package/lib/assets/docs/article/practical-database-design/part4/chapter23.md +0 -2078
  588. package/lib/assets/docs/article/practical-database-design/part4/chapter24.md +0 -2448
  589. package/lib/assets/docs/article/practical-database-design/part4/chapter25.md +0 -3670
  590. package/lib/assets/docs/article/practical-database-design/part4/chapter26.md +0 -2925
  591. package/lib/assets/docs/article/practical-database-design/part4/chapter27.md +0 -3114
  592. package/lib/assets/docs/article/practical-database-design/part4/chapter28.md +0 -2706
  593. package/lib/assets/docs/article/practical-database-design/part4/chapter29.md +0 -2552
  594. package/lib/assets/docs/article/practical-database-design/part4/chapter30.md +0 -2188
  595. package/lib/assets/docs/article/practical-database-design/part4/chapter31.md +0 -1200
  596. package/lib/assets/docs/article/practical-database-design/part4/chapter32.md +0 -2109
  597. package/lib/assets/docs/article/practical-database-design/part5/chapter33.md +0 -1041
  598. package/lib/assets/docs/article/practical-database-design/part5/chapter34.md +0 -1618
  599. package/lib/assets/docs/article/practical-database-design/part5/chapter35.md +0 -1462
  600. package/lib/assets/docs/article/practical-database-design/part5/chapter36.md +0 -1301
  601. package/lib/assets/docs/article/practical-database-design/part5/chapter37.md +0 -1479
  602. package/lib/assets/docs/article/practical-database-design/part5/chapter38.md +0 -1707
  603. package/lib/assets/docs/article/practical-database-design/part5/chapter39.md +0 -2343
  604. package/lib/assets/docs/article/practical-database-design/study/study2-1.md +0 -1702
  605. package/lib/assets/docs/article/practical-database-design/study/study2-2.md +0 -1356
  606. package/lib/assets/docs/article/practical-database-design/study/study2-3.md +0 -2053
  607. package/lib/assets/docs/article/practical-database-design/study/study2-4.md +0 -2238
  608. package/lib/assets/docs/article/practical-database-design/study/study2-5.md +0 -2427
  609. package/lib/assets/docs/article/practical-database-design/study/study3-1.md +0 -2214
  610. package/lib/assets/docs/article/practical-database-design/study/study3-2.md +0 -2230
  611. package/lib/assets/docs/article/practical-database-design/study/study3-3.md +0 -2262
  612. package/lib/assets/docs/article/practical-database-design/study/study3-4.md +0 -2115
  613. package/lib/assets/docs/article/practical-database-design/study/study3-5.md +0 -2516
  614. package/lib/assets/docs/article/practical-database-design/study/study4-1.md +0 -2596
  615. package/lib/assets/docs/article/practical-database-design/study/study4-2.md +0 -2084
  616. package/lib/assets/docs/article/practical-database-design/study/study4-3.md +0 -1814
  617. package/lib/assets/docs/article/practical-database-design/study/study4-4.md +0 -1904
  618. 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
- モノリスアーキテクチャは決して「古い」アーキテクチャではありません。適切な場面で選択することで、シンプルかつ効率的なシステム開発が可能です。