@k2works/claude-code-booster 4.13.0 → 4.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (752) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +42 -42
  3. package/bin/claude-code-booster +113 -90
  4. package/lib/assets/.claude/README.md +287 -283
  5. package/lib/assets/.claude/agents/xp-architect.md +250 -250
  6. package/lib/assets/.claude/agents/xp-executive.md +207 -207
  7. package/lib/assets/.claude/agents/xp-interaction-designer.md +239 -239
  8. package/lib/assets/.claude/agents/xp-product-manager.md +245 -245
  9. package/lib/assets/.claude/agents/xp-programmer.md +268 -268
  10. package/lib/assets/.claude/agents/xp-project-manager.md +229 -229
  11. package/lib/assets/.claude/agents/xp-technical-writer.md +224 -224
  12. package/lib/assets/.claude/agents/xp-tester.md +265 -265
  13. package/lib/assets/.claude/agents/xp-user-representative.md +204 -204
  14. package/lib/assets/.claude/scripts/generate-inception-deck.mjs +911 -911
  15. package/lib/assets/.claude/settings.json +11 -11
  16. package/lib/assets/.claude/skills/ai-agent-guidelines/SKILL.md +111 -111
  17. package/lib/assets/.claude/skills/analyzing-architecture/SKILL.md +83 -83
  18. package/lib/assets/.claude/skills/analyzing-business-architecture/SKILL.md +122 -122
  19. package/lib/assets/.claude/skills/analyzing-business-case/SKILL.md +148 -148
  20. package/lib/assets/.claude/skills/analyzing-business-strategy/SKILL.md +277 -277
  21. package/lib/assets/.claude/skills/analyzing-data-model/SKILL.md +77 -77
  22. package/lib/assets/.claude/skills/analyzing-domain-model/SKILL.md +117 -117
  23. package/lib/assets/.claude/skills/analyzing-inception-deck/SKILL.md +84 -84
  24. package/lib/assets/.claude/skills/analyzing-non-functional/SKILL.md +95 -95
  25. package/lib/assets/.claude/skills/analyzing-operation/SKILL.md +95 -95
  26. package/lib/assets/.claude/skills/analyzing-requirements/SKILL.md +91 -91
  27. package/lib/assets/.claude/skills/analyzing-review/SKILL.md +174 -174
  28. package/lib/assets/.claude/skills/analyzing-tech-stack/SKILL.md +101 -101
  29. package/lib/assets/.claude/skills/analyzing-test-strategy/SKILL.md +89 -89
  30. package/lib/assets/.claude/skills/analyzing-ui-design/SKILL.md +80 -80
  31. package/lib/assets/.claude/skills/analyzing-usecases/SKILL.md +72 -72
  32. package/lib/assets/.claude/skills/closing-iteration/SKILL.md +160 -146
  33. package/lib/assets/.claude/skills/creating-adr/SKILL.md +113 -113
  34. package/lib/assets/.claude/skills/creating-article/SKILL.md +156 -0
  35. package/lib/assets/.claude/skills/creating-development-strategy/SKILL.md +127 -127
  36. package/lib/assets/.claude/skills/creating-iteration-report/SKILL.md +210 -210
  37. package/lib/assets/.claude/skills/creating-journal/SKILL.md +129 -129
  38. package/lib/assets/.claude/skills/creating-manual/SKILL.md +199 -0
  39. package/lib/assets/.claude/skills/creating-release-report/SKILL.md +161 -161
  40. package/lib/assets/.claude/skills/creating-slides/SKILL.md +86 -86
  41. package/lib/assets/.claude/skills/creating-slides/agents/openai.yaml +4 -4
  42. package/lib/assets/.claude/skills/creating-slides/references/manga-css.md +45 -45
  43. package/lib/assets/.claude/skills/creating-slides/scripts/slide_tools.py +239 -239
  44. package/lib/assets/.claude/skills/developing-backend/SKILL.md +100 -100
  45. package/lib/assets/.claude/skills/developing-frontend/SKILL.md +93 -93
  46. package/lib/assets/.claude/skills/developing-release/SKILL.md +120 -120
  47. package/lib/assets/.claude/skills/developing-review/SKILL.md +175 -175
  48. package/lib/assets/.claude/skills/developing-uiux-review/SKILL.md +207 -207
  49. package/lib/assets/.claude/skills/generating-bmc/SKILL.md +123 -123
  50. package/lib/assets/.claude/skills/generating-slides/SKILL.md +94 -94
  51. package/lib/assets/.claude/skills/git-commit/SKILL.md +81 -81
  52. package/lib/assets/.claude/skills/killing-processes/SKILL.md +44 -44
  53. package/lib/assets/.claude/skills/opening-iteration/SKILL.md +109 -105
  54. package/lib/assets/.claude/skills/operating-backup/SKILL.md +59 -59
  55. package/lib/assets/.claude/skills/operating-cicd/SKILL.md +54 -54
  56. package/lib/assets/.claude/skills/operating-deploy/SKILL.md +67 -67
  57. package/lib/assets/.claude/skills/operating-docs/SKILL.md +219 -219
  58. package/lib/assets/.claude/skills/operating-provision/SKILL.md +77 -77
  59. package/lib/assets/.claude/skills/operating-qt/SKILL.md +147 -147
  60. package/lib/assets/.claude/skills/operating-review/SKILL.md +171 -171
  61. package/lib/assets/.claude/skills/operating-script/SKILL.md +145 -145
  62. package/lib/assets/.claude/skills/operating-setup/SKILL.md +63 -63
  63. package/lib/assets/.claude/skills/orchestrating-analysis/SKILL.md +104 -104
  64. package/lib/assets/.claude/skills/orchestrating-development/SKILL.md +168 -168
  65. package/lib/assets/.claude/skills/orchestrating-operation/SKILL.md +158 -158
  66. package/lib/assets/.claude/skills/orchestrating-project/SKILL.md +144 -144
  67. package/lib/assets/.claude/skills/planning-releases/SKILL.md +119 -119
  68. package/lib/assets/.claude/skills/practicing-getting-start-tdd/SKILL.md +266 -266
  69. package/lib/assets/.claude/skills/syncing-github-project/SKILL.md +151 -151
  70. package/lib/assets/.claude/skills/tracking-progress/SKILL.md +91 -91
  71. package/lib/assets/.claude/skills/validating-design/SKILL.md +130 -130
  72. package/lib/assets/.claude/skills/validating-iteration-plan/SKILL.md +238 -238
  73. package/lib/assets/.claude/skills/validating-operating-setup/SKILL.md +118 -118
  74. package/lib/assets/.devcontainer/devcontainer.json +37 -37
  75. package/lib/assets/.env.example +23 -17
  76. package/lib/assets/.gitattributes +4 -4
  77. package/lib/assets/.github/workflows/docker-publish.yml +77 -77
  78. package/lib/assets/.github/workflows/mkdocs.yml +39 -39
  79. package/lib/assets/AGENTS.md +94 -94
  80. package/lib/assets/CLAUDE.md +201 -197
  81. package/lib/assets/README.md +304 -291
  82. package/lib/assets/docker-compose.yml +33 -33
  83. package/lib/assets/docs/adr/index.md +10 -10
  84. package/lib/assets/docs/article/functional-desgin-ppp/all/01-immutability-and-data-transformation.md +475 -475
  85. package/lib/assets/docs/article/functional-desgin-ppp/all/02-function-composition.md +519 -519
  86. package/lib/assets/docs/article/functional-desgin-ppp/all/03-polymorphism.md +537 -537
  87. package/lib/assets/docs/article/functional-desgin-ppp/all/04-data-validation.md +300 -300
  88. package/lib/assets/docs/article/functional-desgin-ppp/all/05-property-based-testing.md +320 -320
  89. package/lib/assets/docs/article/functional-desgin-ppp/all/06-tdd-and-functional.md +498 -498
  90. package/lib/assets/docs/article/functional-desgin-ppp/all/07-composite-pattern.md +298 -298
  91. package/lib/assets/docs/article/functional-desgin-ppp/all/08-decorator-pattern.md +291 -291
  92. package/lib/assets/docs/article/functional-desgin-ppp/all/09-adapter-pattern.md +336 -336
  93. package/lib/assets/docs/article/functional-desgin-ppp/all/10-strategy-pattern.md +303 -303
  94. package/lib/assets/docs/article/functional-desgin-ppp/all/11-command-pattern.md +286 -286
  95. package/lib/assets/docs/article/functional-desgin-ppp/all/12-visitor-pattern.md +322 -322
  96. package/lib/assets/docs/article/functional-desgin-ppp/all/13-abstract-factory-pattern.md +319 -319
  97. package/lib/assets/docs/article/functional-desgin-ppp/all/14-abstract-server-pattern.md +365 -365
  98. package/lib/assets/docs/article/functional-desgin-ppp/all/15-gossiping-bus-drivers.md +156 -156
  99. package/lib/assets/docs/article/functional-desgin-ppp/all/16-payroll-system.md +178 -178
  100. package/lib/assets/docs/article/functional-desgin-ppp/all/17-video-rental-system.md +312 -312
  101. package/lib/assets/docs/article/functional-desgin-ppp/all/18-concurrency-system.md +287 -287
  102. package/lib/assets/docs/article/functional-desgin-ppp/all/19-wa-tor-simulation.md +286 -286
  103. package/lib/assets/docs/article/functional-desgin-ppp/all/20-pattern-interactions.md +274 -274
  104. package/lib/assets/docs/article/functional-desgin-ppp/all/21-best-practices.md +294 -294
  105. package/lib/assets/docs/article/functional-desgin-ppp/all/22-oo-to-fp-migration.md +337 -337
  106. package/lib/assets/docs/article/functional-desgin-ppp/all/index.md +388 -388
  107. package/lib/assets/docs/article/functional-desgin-ppp/clojure/01-immutability-and-data-transformation.md +273 -273
  108. package/lib/assets/docs/article/functional-desgin-ppp/clojure/02-function-composition.md +380 -380
  109. package/lib/assets/docs/article/functional-desgin-ppp/clojure/03-polymorphism.md +384 -384
  110. package/lib/assets/docs/article/functional-desgin-ppp/clojure/04-clojure-spec.md +350 -350
  111. package/lib/assets/docs/article/functional-desgin-ppp/clojure/05-property-based-testing.md +352 -352
  112. package/lib/assets/docs/article/functional-desgin-ppp/clojure/06-tdd-in-functional.md +383 -383
  113. package/lib/assets/docs/article/functional-desgin-ppp/clojure/07-composite-pattern.md +529 -529
  114. package/lib/assets/docs/article/functional-desgin-ppp/clojure/08-decorator-pattern.md +395 -395
  115. package/lib/assets/docs/article/functional-desgin-ppp/clojure/09-adapter-pattern.md +399 -399
  116. package/lib/assets/docs/article/functional-desgin-ppp/clojure/10-strategy-pattern.md +485 -485
  117. package/lib/assets/docs/article/functional-desgin-ppp/clojure/11-command-pattern.md +566 -566
  118. package/lib/assets/docs/article/functional-desgin-ppp/clojure/12-visitor-pattern.md +567 -567
  119. package/lib/assets/docs/article/functional-desgin-ppp/clojure/13-abstract-factory-pattern.md +475 -475
  120. package/lib/assets/docs/article/functional-desgin-ppp/clojure/14-abstract-server-pattern.md +462 -462
  121. package/lib/assets/docs/article/functional-desgin-ppp/clojure/15-gossiping-bus-drivers.md +325 -325
  122. package/lib/assets/docs/article/functional-desgin-ppp/clojure/16-payroll-system.md +401 -401
  123. package/lib/assets/docs/article/functional-desgin-ppp/clojure/17-video-rental-system.md +450 -450
  124. package/lib/assets/docs/article/functional-desgin-ppp/clojure/18-concurrency-system.md +475 -475
  125. package/lib/assets/docs/article/functional-desgin-ppp/clojure/19-wator-simulation.md +739 -739
  126. package/lib/assets/docs/article/functional-desgin-ppp/clojure/20-pattern-interactions.md +567 -567
  127. package/lib/assets/docs/article/functional-desgin-ppp/clojure/21-best-practices.md +518 -518
  128. package/lib/assets/docs/article/functional-desgin-ppp/clojure/22-oo-to-fp-migration.md +532 -532
  129. package/lib/assets/docs/article/functional-desgin-ppp/clojure/index.md +241 -241
  130. package/lib/assets/docs/article/functional-desgin-ppp/elixir/01-immutability-and-data-transformation.md +383 -383
  131. package/lib/assets/docs/article/functional-desgin-ppp/elixir/02-function-composition.md +374 -374
  132. package/lib/assets/docs/article/functional-desgin-ppp/elixir/03-polymorphism.md +375 -375
  133. package/lib/assets/docs/article/functional-desgin-ppp/elixir/04-data-validation.md +195 -195
  134. package/lib/assets/docs/article/functional-desgin-ppp/elixir/05-property-based-testing.md +268 -268
  135. package/lib/assets/docs/article/functional-desgin-ppp/elixir/06-tdd-and-fp.md +294 -294
  136. package/lib/assets/docs/article/functional-desgin-ppp/elixir/07-effects-and-pure-functions.md +164 -164
  137. package/lib/assets/docs/article/functional-desgin-ppp/elixir/08-error-handling-strategies.md +168 -168
  138. package/lib/assets/docs/article/functional-desgin-ppp/elixir/09-io-and-external-systems.md +254 -254
  139. package/lib/assets/docs/article/functional-desgin-ppp/elixir/10-concurrency-patterns.md +269 -269
  140. package/lib/assets/docs/article/functional-desgin-ppp/elixir/11-command-pattern.md +148 -148
  141. package/lib/assets/docs/article/functional-desgin-ppp/elixir/12-visitor-pattern.md +176 -176
  142. package/lib/assets/docs/article/functional-desgin-ppp/elixir/13-abstract-factory-pattern.md +604 -604
  143. package/lib/assets/docs/article/functional-desgin-ppp/elixir/14-abstract-server-pattern.md +729 -729
  144. package/lib/assets/docs/article/functional-desgin-ppp/elixir/15-gossiping-bus-drivers.md +291 -291
  145. package/lib/assets/docs/article/functional-desgin-ppp/elixir/16-payroll-system.md +420 -420
  146. package/lib/assets/docs/article/functional-desgin-ppp/elixir/17-video-rental-system.md +319 -319
  147. package/lib/assets/docs/article/functional-desgin-ppp/elixir/18-concurrency-system.md +466 -466
  148. package/lib/assets/docs/article/functional-desgin-ppp/elixir/19-wator-simulation.md +523 -523
  149. package/lib/assets/docs/article/functional-desgin-ppp/elixir/20-pattern-interactions.md +287 -287
  150. package/lib/assets/docs/article/functional-desgin-ppp/elixir/21-best-practices.md +340 -340
  151. package/lib/assets/docs/article/functional-desgin-ppp/elixir/22-oo-to-fp-migration.md +395 -395
  152. package/lib/assets/docs/article/functional-desgin-ppp/elixir/index.md +248 -248
  153. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/01-immutability-and-data-transformation.md +384 -384
  154. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/02-function-composition.md +452 -452
  155. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/03-polymorphism.md +495 -495
  156. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/04-data-validation.md +416 -416
  157. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/05-property-based-testing.md +382 -382
  158. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/06-tdd-functional.md +687 -687
  159. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/07-composite-pattern.md +442 -442
  160. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/08-decorator-pattern.md +479 -479
  161. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/09-adapter-pattern.md +479 -479
  162. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/10-strategy-pattern.md +427 -427
  163. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/11-command-pattern.md +428 -428
  164. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/12-visitor-pattern.md +339 -339
  165. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/13-abstract-factory-pattern.md +309 -309
  166. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/14-abstract-server-pattern.md +596 -596
  167. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/15-gossiping-bus-drivers.md +355 -355
  168. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/16-payroll-system.md +350 -350
  169. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/17-video-rental-system.md +414 -414
  170. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/18-concurrency-system.md +367 -367
  171. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/19-wator-simulation.md +403 -403
  172. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/20-pattern-interactions.md +291 -291
  173. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/21-best-practices.md +324 -324
  174. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/22-oo-to-fp-migration.md +332 -332
  175. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/index.md +274 -274
  176. package/lib/assets/docs/article/functional-desgin-ppp/haskell/01-immutability-and-data-transformation.md +298 -298
  177. package/lib/assets/docs/article/functional-desgin-ppp/haskell/02-function-composition.md +304 -304
  178. package/lib/assets/docs/article/functional-desgin-ppp/haskell/03-polymorphism.md +362 -362
  179. package/lib/assets/docs/article/functional-desgin-ppp/haskell/04-data-validation.md +257 -257
  180. package/lib/assets/docs/article/functional-desgin-ppp/haskell/05-property-based-testing.md +254 -254
  181. package/lib/assets/docs/article/functional-desgin-ppp/haskell/06-tdd-functional.md +283 -283
  182. package/lib/assets/docs/article/functional-desgin-ppp/haskell/07-composite-pattern.md +395 -395
  183. package/lib/assets/docs/article/functional-desgin-ppp/haskell/08-decorator-pattern.md +319 -319
  184. package/lib/assets/docs/article/functional-desgin-ppp/haskell/09-adapter-pattern.md +382 -382
  185. package/lib/assets/docs/article/functional-desgin-ppp/haskell/10-strategy-pattern.md +287 -287
  186. package/lib/assets/docs/article/functional-desgin-ppp/haskell/11-command-pattern.md +303 -303
  187. package/lib/assets/docs/article/functional-desgin-ppp/haskell/12-visitor-pattern.md +326 -326
  188. package/lib/assets/docs/article/functional-desgin-ppp/haskell/13-abstract-factory-pattern.md +332 -332
  189. package/lib/assets/docs/article/functional-desgin-ppp/haskell/14-abstract-server-pattern.md +379 -379
  190. package/lib/assets/docs/article/functional-desgin-ppp/haskell/15-gossiping-bus-drivers.md +177 -177
  191. package/lib/assets/docs/article/functional-desgin-ppp/haskell/16-payroll-system.md +219 -219
  192. package/lib/assets/docs/article/functional-desgin-ppp/haskell/17-video-rental-system.md +244 -244
  193. package/lib/assets/docs/article/functional-desgin-ppp/haskell/18-concurrency-system.md +363 -363
  194. package/lib/assets/docs/article/functional-desgin-ppp/haskell/19-wator-simulation.md +438 -438
  195. package/lib/assets/docs/article/functional-desgin-ppp/haskell/20-pattern-interactions.md +325 -325
  196. package/lib/assets/docs/article/functional-desgin-ppp/haskell/21-best-practices.md +403 -403
  197. package/lib/assets/docs/article/functional-desgin-ppp/haskell/22-oo-to-fp-migration.md +469 -469
  198. package/lib/assets/docs/article/functional-desgin-ppp/haskell/index.md +174 -174
  199. package/lib/assets/docs/article/functional-desgin-ppp/index.md +90 -90
  200. package/lib/assets/docs/article/functional-desgin-ppp/rust/01-immutability-and-data-transformation.md +450 -450
  201. package/lib/assets/docs/article/functional-desgin-ppp/rust/02-function-composition.md +463 -463
  202. package/lib/assets/docs/article/functional-desgin-ppp/rust/03-polymorphism.md +425 -425
  203. package/lib/assets/docs/article/functional-desgin-ppp/rust/04-data-validation.md +273 -273
  204. package/lib/assets/docs/article/functional-desgin-ppp/rust/05-property-based-testing.md +247 -247
  205. package/lib/assets/docs/article/functional-desgin-ppp/rust/06-tdd-and-functional.md +841 -841
  206. package/lib/assets/docs/article/functional-desgin-ppp/rust/07-composite-pattern.md +384 -384
  207. package/lib/assets/docs/article/functional-desgin-ppp/rust/08-decorator-pattern.md +383 -383
  208. package/lib/assets/docs/article/functional-desgin-ppp/rust/09-adapter-pattern.md +339 -339
  209. package/lib/assets/docs/article/functional-desgin-ppp/rust/10-strategy-pattern.md +331 -331
  210. package/lib/assets/docs/article/functional-desgin-ppp/rust/11-command-pattern.md +356 -356
  211. package/lib/assets/docs/article/functional-desgin-ppp/rust/12-visitor-pattern.md +379 -379
  212. package/lib/assets/docs/article/functional-desgin-ppp/rust/13-abstract-factory-pattern.md +361 -361
  213. package/lib/assets/docs/article/functional-desgin-ppp/rust/14-abstract-server-pattern.md +392 -392
  214. package/lib/assets/docs/article/functional-desgin-ppp/rust/15-gossiping-bus-drivers.md +300 -300
  215. package/lib/assets/docs/article/functional-desgin-ppp/rust/16-payroll-system.md +297 -297
  216. package/lib/assets/docs/article/functional-desgin-ppp/rust/17-video-rental-system.md +304 -304
  217. package/lib/assets/docs/article/functional-desgin-ppp/rust/18-concurrency-system.md +315 -315
  218. package/lib/assets/docs/article/functional-desgin-ppp/rust/19-wator-simulation.md +311 -311
  219. package/lib/assets/docs/article/functional-desgin-ppp/rust/20-pattern-interactions.md +304 -304
  220. package/lib/assets/docs/article/functional-desgin-ppp/rust/21-best-practices.md +336 -336
  221. package/lib/assets/docs/article/functional-desgin-ppp/rust/22-oo-to-fp-migration.md +349 -349
  222. package/lib/assets/docs/article/functional-desgin-ppp/rust/index.md +243 -243
  223. package/lib/assets/docs/article/functional-desgin-ppp/scala/01-immutability-and-data-transformation.md +328 -328
  224. package/lib/assets/docs/article/functional-desgin-ppp/scala/02-function-composition.md +348 -348
  225. package/lib/assets/docs/article/functional-desgin-ppp/scala/03-polymorphism.md +357 -357
  226. package/lib/assets/docs/article/functional-desgin-ppp/scala/04-data-validation.md +364 -364
  227. package/lib/assets/docs/article/functional-desgin-ppp/scala/05-property-based-testing.md +515 -515
  228. package/lib/assets/docs/article/functional-desgin-ppp/scala/06-tdd-functional.md +557 -557
  229. package/lib/assets/docs/article/functional-desgin-ppp/scala/07-composite-pattern.md +363 -363
  230. package/lib/assets/docs/article/functional-desgin-ppp/scala/08-decorator-pattern.md +327 -327
  231. package/lib/assets/docs/article/functional-desgin-ppp/scala/09-adapter-pattern.md +517 -517
  232. package/lib/assets/docs/article/functional-desgin-ppp/scala/10-strategy-pattern.md +441 -441
  233. package/lib/assets/docs/article/functional-desgin-ppp/scala/11-command-pattern.md +407 -407
  234. package/lib/assets/docs/article/functional-desgin-ppp/scala/12-visitor-pattern.md +379 -379
  235. package/lib/assets/docs/article/functional-desgin-ppp/scala/13-abstract-factory-pattern.md +398 -398
  236. package/lib/assets/docs/article/functional-desgin-ppp/scala/14-abstract-server-pattern.md +476 -476
  237. package/lib/assets/docs/article/functional-desgin-ppp/scala/15-gossiping-bus-drivers.md +391 -391
  238. package/lib/assets/docs/article/functional-desgin-ppp/scala/16-payroll-system.md +342 -342
  239. package/lib/assets/docs/article/functional-desgin-ppp/scala/17-video-rental-system.md +324 -324
  240. package/lib/assets/docs/article/functional-desgin-ppp/scala/18-concurrency-system.md +730 -730
  241. package/lib/assets/docs/article/functional-desgin-ppp/scala/19-wator-simulation.md +624 -624
  242. package/lib/assets/docs/article/functional-desgin-ppp/scala/20-pattern-interactions.md +512 -512
  243. package/lib/assets/docs/article/functional-desgin-ppp/scala/21-best-practices.md +433 -433
  244. package/lib/assets/docs/article/functional-desgin-ppp/scala/22-oo-to-fp-migration.md +688 -688
  245. package/lib/assets/docs/article/functional-desgin-ppp/scala/index.md +243 -243
  246. package/lib/assets/docs/article/getting-start-tdd/clojure/01-todo-list-and-first-test.md +166 -166
  247. package/lib/assets/docs/article/getting-start-tdd/clojure/02-fake-it-and-triangulation.md +162 -162
  248. package/lib/assets/docs/article/getting-start-tdd/clojure/03-obvious-implementation-and-refactoring.md +135 -135
  249. package/lib/assets/docs/article/getting-start-tdd/clojure/04-version-control-and-conventional-commits.md +88 -88
  250. package/lib/assets/docs/article/getting-start-tdd/clojure/05-package-management-and-static-analysis.md +299 -299
  251. package/lib/assets/docs/article/getting-start-tdd/clojure/06-task-runner-and-ci-cd.md +241 -241
  252. package/lib/assets/docs/article/getting-start-tdd/clojure/07-protocols-and-records.md +131 -131
  253. package/lib/assets/docs/article/getting-start-tdd/clojure/08-multimethods-and-design-patterns.md +130 -130
  254. package/lib/assets/docs/article/getting-start-tdd/clojure/09-namespaces-and-module-design.md +127 -127
  255. package/lib/assets/docs/article/getting-start-tdd/clojure/10-higher-order-functions-and-composition.md +114 -114
  256. package/lib/assets/docs/article/getting-start-tdd/clojure/11-persistent-data-and-pipeline.md +138 -138
  257. package/lib/assets/docs/article/getting-start-tdd/clojure/12-error-handling-and-spec.md +161 -161
  258. package/lib/assets/docs/article/getting-start-tdd/clojure/index.md +65 -65
  259. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter01.md +232 -232
  260. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter02.md +244 -244
  261. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter03.md +202 -202
  262. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter04.md +92 -92
  263. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter05.md +256 -256
  264. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter06.md +195 -195
  265. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter07.md +214 -214
  266. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter08.md +249 -249
  267. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter09.md +174 -174
  268. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter10.md +166 -166
  269. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter11.md +192 -192
  270. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter12.md +211 -211
  271. package/lib/assets/docs/article/getting-start-tdd/csharp/index.md +83 -83
  272. package/lib/assets/docs/article/getting-start-tdd/elixir/01-todo-list-and-first-test.md +87 -87
  273. package/lib/assets/docs/article/getting-start-tdd/elixir/02-fake-it-and-triangulation.md +95 -95
  274. package/lib/assets/docs/article/getting-start-tdd/elixir/03-obvious-implementation-and-refactoring.md +109 -109
  275. package/lib/assets/docs/article/getting-start-tdd/elixir/04-version-control-and-conventional-commits.md +96 -96
  276. package/lib/assets/docs/article/getting-start-tdd/elixir/05-package-management-and-static-analysis.md +88 -88
  277. package/lib/assets/docs/article/getting-start-tdd/elixir/06-task-runner-and-ci-cd.md +71 -71
  278. package/lib/assets/docs/article/getting-start-tdd/elixir/07-structs-and-protocols.md +110 -110
  279. package/lib/assets/docs/article/getting-start-tdd/elixir/08-pattern-matching-and-guards.md +108 -108
  280. package/lib/assets/docs/article/getting-start-tdd/elixir/09-module-design-and-behaviours.md +104 -104
  281. package/lib/assets/docs/article/getting-start-tdd/elixir/10-higher-order-functions-and-pipeline.md +178 -178
  282. package/lib/assets/docs/article/getting-start-tdd/elixir/11-stream-and-lazy-evaluation.md +142 -142
  283. package/lib/assets/docs/article/getting-start-tdd/elixir/12-error-handling-and-with.md +145 -145
  284. package/lib/assets/docs/article/getting-start-tdd/elixir/index.md +35 -35
  285. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter01.md +202 -202
  286. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter02.md +246 -246
  287. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter03.md +218 -218
  288. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter04.md +179 -179
  289. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter05.md +267 -267
  290. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter06.md +190 -190
  291. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter07.md +161 -161
  292. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter08.md +175 -175
  293. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter09.md +222 -222
  294. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter10.md +189 -189
  295. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter11.md +212 -212
  296. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter12.md +215 -215
  297. package/lib/assets/docs/article/getting-start-tdd/fsharp/index.md +71 -71
  298. package/lib/assets/docs/article/getting-start-tdd/go/01-todo-list-and-first-test.md +213 -213
  299. package/lib/assets/docs/article/getting-start-tdd/go/02-fake-it-and-triangulation.md +302 -302
  300. package/lib/assets/docs/article/getting-start-tdd/go/03-obvious-implementation-and-refactoring.md +339 -339
  301. package/lib/assets/docs/article/getting-start-tdd/go/04-version-control-and-conventional-commits.md +112 -112
  302. package/lib/assets/docs/article/getting-start-tdd/go/05-package-management-and-static-analysis.md +272 -272
  303. package/lib/assets/docs/article/getting-start-tdd/go/06-task-runner-and-ci-cd.md +233 -233
  304. package/lib/assets/docs/article/getting-start-tdd/go/07-encapsulation-and-polymorphism.md +394 -394
  305. package/lib/assets/docs/article/getting-start-tdd/go/08-design-patterns.md +422 -422
  306. package/lib/assets/docs/article/getting-start-tdd/go/09-solid-principles-and-module-design.md +400 -400
  307. package/lib/assets/docs/article/getting-start-tdd/go/10-higher-order-functions-and-composition.md +226 -226
  308. package/lib/assets/docs/article/getting-start-tdd/go/11-immutable-data-and-pipeline.md +296 -296
  309. package/lib/assets/docs/article/getting-start-tdd/go/12-error-handling-and-type-safety.md +411 -411
  310. package/lib/assets/docs/article/getting-start-tdd/go/index.md +83 -83
  311. package/lib/assets/docs/article/getting-start-tdd/haskell/01-todo-list-and-first-test.md +279 -279
  312. package/lib/assets/docs/article/getting-start-tdd/haskell/02-fake-it-and-triangulation.md +337 -337
  313. package/lib/assets/docs/article/getting-start-tdd/haskell/03-obvious-implementation-and-refactoring.md +257 -257
  314. package/lib/assets/docs/article/getting-start-tdd/haskell/04-version-control-and-conventional-commits.md +182 -182
  315. package/lib/assets/docs/article/getting-start-tdd/haskell/05-package-management-and-static-analysis.md +313 -313
  316. package/lib/assets/docs/article/getting-start-tdd/haskell/06-task-runner-and-ci-cd.md +309 -309
  317. package/lib/assets/docs/article/getting-start-tdd/haskell/07-algebraic-data-types-and-type-classes.md +412 -412
  318. package/lib/assets/docs/article/getting-start-tdd/haskell/08-pattern-matching-and-guards.md +390 -390
  319. package/lib/assets/docs/article/getting-start-tdd/haskell/09-module-design-and-smart-constructors.md +461 -461
  320. package/lib/assets/docs/article/getting-start-tdd/haskell/10-higher-order-functions-and-currying.md +434 -434
  321. package/lib/assets/docs/article/getting-start-tdd/haskell/11-function-composition-and-point-free.md +392 -392
  322. package/lib/assets/docs/article/getting-start-tdd/haskell/12-monad-and-error-handling.md +631 -631
  323. package/lib/assets/docs/article/getting-start-tdd/haskell/index.md +49 -49
  324. package/lib/assets/docs/article/getting-start-tdd/index.md +93 -93
  325. package/lib/assets/docs/article/getting-start-tdd/integration/01-language-overview.md +375 -375
  326. package/lib/assets/docs/article/getting-start-tdd/integration/02-test-framework-comparison.md +349 -349
  327. package/lib/assets/docs/article/getting-start-tdd/integration/03-tdd-pattern-comparison.md +445 -445
  328. package/lib/assets/docs/article/getting-start-tdd/integration/04-type-system-comparison.md +409 -409
  329. package/lib/assets/docs/article/getting-start-tdd/integration/05-dev-environment-comparison.md +330 -330
  330. package/lib/assets/docs/article/getting-start-tdd/integration/06-learning-roadmap.md +290 -290
  331. package/lib/assets/docs/article/getting-start-tdd/integration/index.md +69 -69
  332. package/lib/assets/docs/article/getting-start-tdd/java/01-todo-list-and-first-test.md +234 -234
  333. package/lib/assets/docs/article/getting-start-tdd/java/02-fake-it-and-triangulation.md +261 -261
  334. package/lib/assets/docs/article/getting-start-tdd/java/03-obvious-implementation-and-refactoring.md +185 -185
  335. package/lib/assets/docs/article/getting-start-tdd/java/04-version-control-and-conventional-commits.md +115 -115
  336. package/lib/assets/docs/article/getting-start-tdd/java/05-package-management-and-static-analysis.md +382 -382
  337. package/lib/assets/docs/article/getting-start-tdd/java/06-task-runner-and-ci-cd.md +272 -272
  338. package/lib/assets/docs/article/getting-start-tdd/java/07-encapsulation-and-polymorphism.md +626 -626
  339. package/lib/assets/docs/article/getting-start-tdd/java/08-design-patterns.md +393 -393
  340. package/lib/assets/docs/article/getting-start-tdd/java/09-solid-principles-and-module-design.md +310 -310
  341. package/lib/assets/docs/article/getting-start-tdd/java/10-higher-order-functions-and-composition.md +188 -188
  342. package/lib/assets/docs/article/getting-start-tdd/java/11-immutable-data-and-pipeline.md +167 -167
  343. package/lib/assets/docs/article/getting-start-tdd/java/12-error-handling-and-type-safety.md +205 -205
  344. package/lib/assets/docs/article/getting-start-tdd/java/index.md +61 -61
  345. package/lib/assets/docs/article/getting-start-tdd/node/01-todo-list-and-first-test.md +244 -244
  346. package/lib/assets/docs/article/getting-start-tdd/node/02-fake-it-and-triangulation.md +262 -262
  347. package/lib/assets/docs/article/getting-start-tdd/node/03-obvious-implementation-and-refactoring.md +169 -169
  348. package/lib/assets/docs/article/getting-start-tdd/node/04-version-control-and-conventional-commits.md +112 -112
  349. package/lib/assets/docs/article/getting-start-tdd/node/05-package-management-and-static-analysis.md +314 -314
  350. package/lib/assets/docs/article/getting-start-tdd/node/06-task-runner-and-ci-cd.md +235 -235
  351. package/lib/assets/docs/article/getting-start-tdd/node/07-encapsulation-and-polymorphism.md +327 -327
  352. package/lib/assets/docs/article/getting-start-tdd/node/08-design-patterns.md +322 -322
  353. package/lib/assets/docs/article/getting-start-tdd/node/09-solid-principles-and-module-design.md +285 -285
  354. package/lib/assets/docs/article/getting-start-tdd/node/10-higher-order-functions-and-composition.md +199 -199
  355. package/lib/assets/docs/article/getting-start-tdd/node/11-immutable-data-and-pipeline.md +207 -207
  356. package/lib/assets/docs/article/getting-start-tdd/node/12-error-handling-and-type-safety.md +295 -295
  357. package/lib/assets/docs/article/getting-start-tdd/node/index.md +56 -56
  358. package/lib/assets/docs/article/getting-start-tdd/php/01-todo-list-and-first-test.md +259 -259
  359. package/lib/assets/docs/article/getting-start-tdd/php/02-fake-it-and-triangulation.md +200 -200
  360. package/lib/assets/docs/article/getting-start-tdd/php/03-obvious-implementation-and-refactoring.md +248 -248
  361. package/lib/assets/docs/article/getting-start-tdd/php/04-version-control-and-conventional-commits.md +141 -141
  362. package/lib/assets/docs/article/getting-start-tdd/php/05-package-management-and-static-analysis.md +410 -410
  363. package/lib/assets/docs/article/getting-start-tdd/php/06-task-runner-and-ci-cd.md +321 -321
  364. package/lib/assets/docs/article/getting-start-tdd/php/07-encapsulation-and-polymorphism.md +372 -372
  365. package/lib/assets/docs/article/getting-start-tdd/php/08-design-patterns.md +453 -453
  366. package/lib/assets/docs/article/getting-start-tdd/php/09-solid-principles-and-module-design.md +460 -460
  367. package/lib/assets/docs/article/getting-start-tdd/php/10-higher-order-functions-and-composition.md +182 -182
  368. package/lib/assets/docs/article/getting-start-tdd/php/11-immutable-data-and-pipeline.md +266 -266
  369. package/lib/assets/docs/article/getting-start-tdd/php/12-error-handling-and-type-safety.md +308 -308
  370. package/lib/assets/docs/article/getting-start-tdd/php/index.md +84 -84
  371. package/lib/assets/docs/article/getting-start-tdd/python/01-todo-list-and-first-test.md +201 -201
  372. package/lib/assets/docs/article/getting-start-tdd/python/02-fake-it-and-triangulation.md +247 -247
  373. package/lib/assets/docs/article/getting-start-tdd/python/03-obvious-implementation-and-refactoring.md +199 -199
  374. package/lib/assets/docs/article/getting-start-tdd/python/04-version-control-and-conventional-commits.md +87 -87
  375. package/lib/assets/docs/article/getting-start-tdd/python/05-package-management-and-static-analysis.md +274 -274
  376. package/lib/assets/docs/article/getting-start-tdd/python/06-task-runner-and-ci-cd.md +190 -190
  377. package/lib/assets/docs/article/getting-start-tdd/python/07-encapsulation-and-polymorphism.md +208 -208
  378. package/lib/assets/docs/article/getting-start-tdd/python/08-design-patterns.md +172 -172
  379. package/lib/assets/docs/article/getting-start-tdd/python/09-solid-principles-and-module-design.md +130 -130
  380. package/lib/assets/docs/article/getting-start-tdd/python/10-higher-order-functions-and-composition.md +122 -122
  381. package/lib/assets/docs/article/getting-start-tdd/python/11-immutable-data-and-pipeline.md +116 -116
  382. package/lib/assets/docs/article/getting-start-tdd/python/12-error-handling-and-type-safety.md +126 -126
  383. package/lib/assets/docs/article/getting-start-tdd/python/index.md +55 -55
  384. package/lib/assets/docs/article/getting-start-tdd/ruby/01-todo-list-and-first-test.md +231 -231
  385. package/lib/assets/docs/article/getting-start-tdd/ruby/02-fake-it-and-triangulation.md +238 -238
  386. package/lib/assets/docs/article/getting-start-tdd/ruby/03-obvious-implementation-and-refactoring.md +228 -228
  387. package/lib/assets/docs/article/getting-start-tdd/ruby/04-version-control-and-conventional-commits.md +112 -112
  388. package/lib/assets/docs/article/getting-start-tdd/ruby/05-package-management-and-static-analysis.md +287 -287
  389. package/lib/assets/docs/article/getting-start-tdd/ruby/06-task-runner-and-ci-cd.md +248 -248
  390. package/lib/assets/docs/article/getting-start-tdd/ruby/07-encapsulation-and-polymorphism.md +279 -279
  391. package/lib/assets/docs/article/getting-start-tdd/ruby/08-design-patterns.md +329 -329
  392. package/lib/assets/docs/article/getting-start-tdd/ruby/09-solid-principles-and-module-design.md +196 -196
  393. package/lib/assets/docs/article/getting-start-tdd/ruby/10-higher-order-functions-and-composition.md +175 -175
  394. package/lib/assets/docs/article/getting-start-tdd/ruby/11-immutable-data-and-pipeline.md +237 -237
  395. package/lib/assets/docs/article/getting-start-tdd/ruby/12-error-handling-and-type-safety.md +398 -398
  396. package/lib/assets/docs/article/getting-start-tdd/ruby/index.md +83 -83
  397. package/lib/assets/docs/article/getting-start-tdd/rust/01-todo-list-and-first-test.md +211 -211
  398. package/lib/assets/docs/article/getting-start-tdd/rust/02-fake-it-and-triangulation.md +264 -264
  399. package/lib/assets/docs/article/getting-start-tdd/rust/03-obvious-implementation-and-refactoring.md +233 -233
  400. package/lib/assets/docs/article/getting-start-tdd/rust/04-version-control-and-conventional-commits.md +92 -92
  401. package/lib/assets/docs/article/getting-start-tdd/rust/05-package-management-and-static-analysis.md +212 -212
  402. package/lib/assets/docs/article/getting-start-tdd/rust/06-task-runner-and-ci-cd.md +164 -164
  403. package/lib/assets/docs/article/getting-start-tdd/rust/07-encapsulation-and-polymorphism.md +142 -142
  404. package/lib/assets/docs/article/getting-start-tdd/rust/08-design-patterns.md +145 -145
  405. package/lib/assets/docs/article/getting-start-tdd/rust/09-solid-principles-and-module-design.md +110 -110
  406. package/lib/assets/docs/article/getting-start-tdd/rust/10-higher-order-functions-and-composition.md +94 -94
  407. package/lib/assets/docs/article/getting-start-tdd/rust/11-immutable-data-and-pipeline.md +105 -105
  408. package/lib/assets/docs/article/getting-start-tdd/rust/12-error-handling-and-type-safety.md +112 -112
  409. package/lib/assets/docs/article/getting-start-tdd/rust/index.md +83 -83
  410. package/lib/assets/docs/article/getting-start-tdd/scala/01-todo-list-and-first-test.md +111 -111
  411. package/lib/assets/docs/article/getting-start-tdd/scala/02-fake-it-and-triangulation.md +107 -107
  412. package/lib/assets/docs/article/getting-start-tdd/scala/03-obvious-implementation-and-refactoring.md +99 -99
  413. package/lib/assets/docs/article/getting-start-tdd/scala/04-version-control-and-conventional-commits.md +123 -123
  414. package/lib/assets/docs/article/getting-start-tdd/scala/05-package-management-and-static-analysis.md +196 -196
  415. package/lib/assets/docs/article/getting-start-tdd/scala/06-task-runner-and-ci-cd.md +186 -186
  416. package/lib/assets/docs/article/getting-start-tdd/scala/07-case-classes-and-traits.md +139 -139
  417. package/lib/assets/docs/article/getting-start-tdd/scala/08-pattern-matching-and-sealed-traits.md +106 -106
  418. package/lib/assets/docs/article/getting-start-tdd/scala/09-packages-and-module-design.md +75 -75
  419. package/lib/assets/docs/article/getting-start-tdd/scala/10-higher-order-functions-and-composition.md +104 -104
  420. package/lib/assets/docs/article/getting-start-tdd/scala/11-collections-and-lazy-evaluation.md +94 -94
  421. package/lib/assets/docs/article/getting-start-tdd/scala/12-error-handling-and-type-safety.md +92 -92
  422. package/lib/assets/docs/article/getting-start-tdd/scala/index.md +65 -65
  423. package/lib/assets/docs/article/grokking-concurrency/all/index.md +404 -404
  424. package/lib/assets/docs/article/grokking-concurrency/all/part-1-ch02-sequential.md +554 -554
  425. package/lib/assets/docs/article/grokking-concurrency/all/part-2-ch04-05-threads.md +469 -469
  426. package/lib/assets/docs/article/grokking-concurrency/all/part-3-ch06-multitasking.md +520 -520
  427. package/lib/assets/docs/article/grokking-concurrency/all/part-4-ch07-parallel-patterns.md +420 -420
  428. package/lib/assets/docs/article/grokking-concurrency/all/part-5-ch08-09-synchronization.md +510 -510
  429. package/lib/assets/docs/article/grokking-concurrency/all/part-6-ch10-11-nonblocking-io.md +435 -435
  430. package/lib/assets/docs/article/grokking-concurrency/all/part-7-ch12-async.md +465 -465
  431. package/lib/assets/docs/article/grokking-concurrency/all/part-8-ch13-mapreduce.md +377 -377
  432. package/lib/assets/docs/article/grokking-concurrency/clojure/index.md +116 -116
  433. package/lib/assets/docs/article/grokking-concurrency/clojure/part-1.md +108 -108
  434. package/lib/assets/docs/article/grokking-concurrency/clojure/part-2.md +101 -101
  435. package/lib/assets/docs/article/grokking-concurrency/clojure/part-3.md +122 -122
  436. package/lib/assets/docs/article/grokking-concurrency/clojure/part-4.md +123 -123
  437. package/lib/assets/docs/article/grokking-concurrency/clojure/part-5.md +118 -118
  438. package/lib/assets/docs/article/grokking-concurrency/clojure/part-6.md +89 -89
  439. package/lib/assets/docs/article/grokking-concurrency/clojure/part-7.md +100 -100
  440. package/lib/assets/docs/article/grokking-concurrency/clojure/part-8.md +120 -120
  441. package/lib/assets/docs/article/grokking-concurrency/csharp/index.md +101 -101
  442. package/lib/assets/docs/article/grokking-concurrency/csharp/part-1.md +97 -97
  443. package/lib/assets/docs/article/grokking-concurrency/csharp/part-2.md +123 -123
  444. package/lib/assets/docs/article/grokking-concurrency/csharp/part-3.md +101 -101
  445. package/lib/assets/docs/article/grokking-concurrency/csharp/part-4.md +112 -112
  446. package/lib/assets/docs/article/grokking-concurrency/csharp/part-5.md +99 -99
  447. package/lib/assets/docs/article/grokking-concurrency/csharp/part-6.md +61 -61
  448. package/lib/assets/docs/article/grokking-concurrency/csharp/part-7.md +84 -84
  449. package/lib/assets/docs/article/grokking-concurrency/csharp/part-8.md +92 -92
  450. package/lib/assets/docs/article/grokking-concurrency/fsharp/index.md +65 -65
  451. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-1.md +80 -80
  452. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-2.md +103 -103
  453. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-3.md +94 -94
  454. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-4.md +110 -110
  455. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-5.md +104 -104
  456. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-6.md +93 -93
  457. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-7.md +121 -121
  458. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-8.md +107 -107
  459. package/lib/assets/docs/article/grokking-concurrency/haskell/index.md +248 -248
  460. package/lib/assets/docs/article/grokking-concurrency/haskell/part-1.md +96 -96
  461. package/lib/assets/docs/article/grokking-concurrency/haskell/part-2.md +96 -96
  462. package/lib/assets/docs/article/grokking-concurrency/haskell/part-3.md +91 -91
  463. package/lib/assets/docs/article/grokking-concurrency/haskell/part-4.md +106 -106
  464. package/lib/assets/docs/article/grokking-concurrency/haskell/part-5.md +99 -99
  465. package/lib/assets/docs/article/grokking-concurrency/haskell/part-6.md +95 -95
  466. package/lib/assets/docs/article/grokking-concurrency/haskell/part-7.md +111 -111
  467. package/lib/assets/docs/article/grokking-concurrency/haskell/part-8.md +118 -118
  468. package/lib/assets/docs/article/grokking-concurrency/index.md +66 -66
  469. package/lib/assets/docs/article/grokking-concurrency/java/index.md +102 -102
  470. package/lib/assets/docs/article/grokking-concurrency/java/part-1.md +308 -308
  471. package/lib/assets/docs/article/grokking-concurrency/java/part-2.md +334 -334
  472. package/lib/assets/docs/article/grokking-concurrency/java/part-3.md +221 -221
  473. package/lib/assets/docs/article/grokking-concurrency/java/part-4.md +213 -213
  474. package/lib/assets/docs/article/grokking-concurrency/java/part-5.md +112 -112
  475. package/lib/assets/docs/article/grokking-concurrency/java/part-6.md +69 -69
  476. package/lib/assets/docs/article/grokking-concurrency/java/part-7.md +101 -101
  477. package/lib/assets/docs/article/grokking-concurrency/java/part-8.md +101 -101
  478. package/lib/assets/docs/article/grokking-concurrency/python/index.md +313 -313
  479. package/lib/assets/docs/article/grokking-concurrency/python/part-1.md +239 -239
  480. package/lib/assets/docs/article/grokking-concurrency/python/part-2.md +418 -418
  481. package/lib/assets/docs/article/grokking-concurrency/python/part-3.md +227 -227
  482. package/lib/assets/docs/article/grokking-concurrency/python/part-4.md +299 -299
  483. package/lib/assets/docs/article/grokking-concurrency/python/part-5.md +315 -315
  484. package/lib/assets/docs/article/grokking-concurrency/python/part-6.md +297 -297
  485. package/lib/assets/docs/article/grokking-concurrency/python/part-7.md +314 -314
  486. package/lib/assets/docs/article/grokking-concurrency/python/part-8.md +360 -360
  487. package/lib/assets/docs/article/grokking-concurrency/rust/index.md +270 -270
  488. package/lib/assets/docs/article/grokking-concurrency/rust/part-1.md +108 -108
  489. package/lib/assets/docs/article/grokking-concurrency/rust/part-2.md +120 -120
  490. package/lib/assets/docs/article/grokking-concurrency/rust/part-3.md +126 -126
  491. package/lib/assets/docs/article/grokking-concurrency/rust/part-4.md +175 -175
  492. package/lib/assets/docs/article/grokking-concurrency/rust/part-5.md +158 -158
  493. package/lib/assets/docs/article/grokking-concurrency/rust/part-6.md +94 -94
  494. package/lib/assets/docs/article/grokking-concurrency/rust/part-7.md +133 -133
  495. package/lib/assets/docs/article/grokking-concurrency/rust/part-8.md +155 -155
  496. package/lib/assets/docs/article/grokking-concurrency/scala/index.md +69 -69
  497. package/lib/assets/docs/article/grokking-concurrency/scala/part-1.md +78 -78
  498. package/lib/assets/docs/article/grokking-concurrency/scala/part-2.md +112 -112
  499. package/lib/assets/docs/article/grokking-concurrency/scala/part-3.md +93 -93
  500. package/lib/assets/docs/article/grokking-concurrency/scala/part-4.md +110 -110
  501. package/lib/assets/docs/article/grokking-concurrency/scala/part-5.md +119 -119
  502. package/lib/assets/docs/article/grokking-concurrency/scala/part-6.md +83 -83
  503. package/lib/assets/docs/article/grokking-concurrency/scala/part-7.md +131 -131
  504. package/lib/assets/docs/article/grokking-concurrency/scala/part-8.md +129 -129
  505. package/lib/assets/docs/article/grokkingfp/all/index.md +368 -368
  506. package/lib/assets/docs/article/grokkingfp/all/part-1-ch01-fp-introduction.md +530 -530
  507. package/lib/assets/docs/article/grokkingfp/all/part-1-ch02-pure-functions.md +923 -923
  508. package/lib/assets/docs/article/grokkingfp/all/part-2-ch03-immutable-data.md +1128 -1128
  509. package/lib/assets/docs/article/grokkingfp/all/part-2-ch04-higher-order-functions.md +1104 -1104
  510. package/lib/assets/docs/article/grokkingfp/all/part-2-ch05-flatmap.md +1026 -1026
  511. package/lib/assets/docs/article/grokkingfp/all/part-3-ch06-option.md +785 -785
  512. package/lib/assets/docs/article/grokkingfp/all/part-3-ch07-either-adt.md +871 -871
  513. package/lib/assets/docs/article/grokkingfp/all/part-4-ch08-io-monad.md +972 -972
  514. package/lib/assets/docs/article/grokkingfp/all/part-4-ch09-streams.md +926 -926
  515. package/lib/assets/docs/article/grokkingfp/all/part-5-ch10-concurrency.md +870 -870
  516. package/lib/assets/docs/article/grokkingfp/all/part-6-ch11-application.md +715 -715
  517. package/lib/assets/docs/article/grokkingfp/all/part-6-ch12-testing.md +626 -626
  518. package/lib/assets/docs/article/grokkingfp/all/writing-plan.md +712 -712
  519. package/lib/assets/docs/article/grokkingfp/clojure/index.md +276 -276
  520. package/lib/assets/docs/article/grokkingfp/clojure/part-1.md +667 -667
  521. package/lib/assets/docs/article/grokkingfp/clojure/part-2.md +643 -643
  522. package/lib/assets/docs/article/grokkingfp/clojure/part-3.md +620 -620
  523. package/lib/assets/docs/article/grokkingfp/clojure/part-4.md +697 -697
  524. package/lib/assets/docs/article/grokkingfp/clojure/part-5.md +751 -751
  525. package/lib/assets/docs/article/grokkingfp/clojure/part-6.md +721 -721
  526. package/lib/assets/docs/article/grokkingfp/csharp/index.md +246 -246
  527. package/lib/assets/docs/article/grokkingfp/csharp/part-1.md +811 -811
  528. package/lib/assets/docs/article/grokkingfp/csharp/part-2.md +971 -971
  529. package/lib/assets/docs/article/grokkingfp/csharp/part-3.md +981 -981
  530. package/lib/assets/docs/article/grokkingfp/csharp/part-4.md +949 -949
  531. package/lib/assets/docs/article/grokkingfp/csharp/part-5.md +947 -947
  532. package/lib/assets/docs/article/grokkingfp/csharp/part-6.md +739 -739
  533. package/lib/assets/docs/article/grokkingfp/elixir/index.md +203 -203
  534. package/lib/assets/docs/article/grokkingfp/elixir/part-1.md +712 -712
  535. package/lib/assets/docs/article/grokkingfp/elixir/part-2.md +838 -838
  536. package/lib/assets/docs/article/grokkingfp/elixir/part-3.md +985 -985
  537. package/lib/assets/docs/article/grokkingfp/elixir/part-4.md +974 -974
  538. package/lib/assets/docs/article/grokkingfp/elixir/part-5.md +1286 -1286
  539. package/lib/assets/docs/article/grokkingfp/elixir/part-6.md +1049 -1049
  540. package/lib/assets/docs/article/grokkingfp/fsharp/index.md +210 -210
  541. package/lib/assets/docs/article/grokkingfp/fsharp/part-1.md +714 -714
  542. package/lib/assets/docs/article/grokkingfp/fsharp/part-2.md +961 -961
  543. package/lib/assets/docs/article/grokkingfp/fsharp/part-3.md +972 -972
  544. package/lib/assets/docs/article/grokkingfp/fsharp/part-4.md +832 -832
  545. package/lib/assets/docs/article/grokkingfp/fsharp/part-5.md +911 -911
  546. package/lib/assets/docs/article/grokkingfp/fsharp/part-6.md +922 -922
  547. package/lib/assets/docs/article/grokkingfp/haskell/index.md +234 -234
  548. package/lib/assets/docs/article/grokkingfp/haskell/part-1.md +591 -591
  549. package/lib/assets/docs/article/grokkingfp/haskell/part-2.md +866 -866
  550. package/lib/assets/docs/article/grokkingfp/haskell/part-3.md +915 -915
  551. package/lib/assets/docs/article/grokkingfp/haskell/part-4.md +878 -878
  552. package/lib/assets/docs/article/grokkingfp/haskell/part-5.md +845 -845
  553. package/lib/assets/docs/article/grokkingfp/haskell/part-6.md +844 -844
  554. package/lib/assets/docs/article/grokkingfp/index.md +143 -143
  555. package/lib/assets/docs/article/grokkingfp/java/index.md +211 -211
  556. package/lib/assets/docs/article/grokkingfp/java/part-1.md +648 -648
  557. package/lib/assets/docs/article/grokkingfp/java/part-2.md +675 -675
  558. package/lib/assets/docs/article/grokkingfp/java/part-3.md +672 -672
  559. package/lib/assets/docs/article/grokkingfp/java/part-4.md +771 -771
  560. package/lib/assets/docs/article/grokkingfp/java/part-5.md +959 -959
  561. package/lib/assets/docs/article/grokkingfp/java/part-6.md +1328 -1328
  562. package/lib/assets/docs/article/grokkingfp/python/index.md +258 -258
  563. package/lib/assets/docs/article/grokkingfp/python/part-1.md +443 -443
  564. package/lib/assets/docs/article/grokkingfp/python/part-2.md +958 -958
  565. package/lib/assets/docs/article/grokkingfp/python/part-3.md +1004 -1004
  566. package/lib/assets/docs/article/grokkingfp/python/part-4.md +765 -765
  567. package/lib/assets/docs/article/grokkingfp/python/part-5.md +747 -747
  568. package/lib/assets/docs/article/grokkingfp/python/part-6.md +861 -861
  569. package/lib/assets/docs/article/grokkingfp/ruby/index.md +330 -330
  570. package/lib/assets/docs/article/grokkingfp/ruby/part-1.md +755 -755
  571. package/lib/assets/docs/article/grokkingfp/ruby/part-2.md +938 -938
  572. package/lib/assets/docs/article/grokkingfp/ruby/part-3.md +946 -946
  573. package/lib/assets/docs/article/grokkingfp/ruby/part-4.md +921 -921
  574. package/lib/assets/docs/article/grokkingfp/ruby/part-5.md +908 -908
  575. package/lib/assets/docs/article/grokkingfp/ruby/part-6.md +1412 -1412
  576. package/lib/assets/docs/article/grokkingfp/rust/index.md +242 -242
  577. package/lib/assets/docs/article/grokkingfp/rust/part-1.md +634 -634
  578. package/lib/assets/docs/article/grokkingfp/rust/part-2.md +1060 -1060
  579. package/lib/assets/docs/article/grokkingfp/rust/part-3.md +994 -994
  580. package/lib/assets/docs/article/grokkingfp/rust/part-4.md +573 -573
  581. package/lib/assets/docs/article/grokkingfp/rust/part-5.md +705 -705
  582. package/lib/assets/docs/article/grokkingfp/rust/part-6.md +508 -508
  583. package/lib/assets/docs/article/grokkingfp/scala/index.md +171 -171
  584. package/lib/assets/docs/article/grokkingfp/scala/part-1.md +543 -543
  585. package/lib/assets/docs/article/grokkingfp/scala/part-2.md +946 -946
  586. package/lib/assets/docs/article/grokkingfp/scala/part-3.md +919 -919
  587. package/lib/assets/docs/article/grokkingfp/scala/part-4.md +742 -742
  588. package/lib/assets/docs/article/grokkingfp/scala/part-5.md +722 -722
  589. package/lib/assets/docs/article/grokkingfp/scala/part-6.md +867 -867
  590. package/lib/assets/docs/article/grokkingfp/typescript/index.md +273 -273
  591. package/lib/assets/docs/article/grokkingfp/typescript/part-1.md +561 -561
  592. package/lib/assets/docs/article/grokkingfp/typescript/part-2.md +1129 -1129
  593. package/lib/assets/docs/article/grokkingfp/typescript/part-3.md +842 -842
  594. package/lib/assets/docs/article/grokkingfp/typescript/part-4.md +1087 -1087
  595. package/lib/assets/docs/article/grokkingfp/typescript/part-5.md +717 -717
  596. package/lib/assets/docs/article/grokkingfp/typescript/part-6.md +982 -982
  597. package/lib/assets/docs/article/index.md +41 -41
  598. package/lib/assets/docs/article/practical-database-design/index.md +121 -121
  599. package/lib/assets/docs/article/practical-database-design/part1/chapter01.md +288 -288
  600. package/lib/assets/docs/article/practical-database-design/part1/chapter02.md +518 -518
  601. package/lib/assets/docs/article/practical-database-design/part1/chapter03.md +557 -557
  602. package/lib/assets/docs/article/practical-database-design/part2/chapter04.md +924 -924
  603. package/lib/assets/docs/article/practical-database-design/part2/chapter05.md +1627 -1627
  604. package/lib/assets/docs/article/practical-database-design/part2/chapter06.md +2716 -2716
  605. package/lib/assets/docs/article/practical-database-design/part2/chapter07.md +2082 -2082
  606. package/lib/assets/docs/article/practical-database-design/part2/chapter08.md +2105 -2105
  607. package/lib/assets/docs/article/practical-database-design/part2/chapter09.md +2031 -2031
  608. package/lib/assets/docs/article/practical-database-design/part2/chapter10.md +1387 -1387
  609. package/lib/assets/docs/article/practical-database-design/part2/chapter11.md +1677 -1677
  610. package/lib/assets/docs/article/practical-database-design/part2/chapter12.md +1417 -1417
  611. package/lib/assets/docs/article/practical-database-design/part2/chapter13.md +1434 -1434
  612. package/lib/assets/docs/article/practical-database-design/part3/chapter14.md +667 -667
  613. package/lib/assets/docs/article/practical-database-design/part3/chapter15.md +1625 -1625
  614. package/lib/assets/docs/article/practical-database-design/part3/chapter16.md +1915 -1915
  615. package/lib/assets/docs/article/practical-database-design/part3/chapter17.md +1708 -1708
  616. package/lib/assets/docs/article/practical-database-design/part3/chapter18.md +2095 -2095
  617. package/lib/assets/docs/article/practical-database-design/part3/chapter19.md +1123 -1123
  618. package/lib/assets/docs/article/practical-database-design/part3/chapter20.md +1031 -1031
  619. package/lib/assets/docs/article/practical-database-design/part3/chapter21.md +1382 -1382
  620. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter14-orm.md +991 -991
  621. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter15-orm.md +1300 -1300
  622. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter16-orm.md +1166 -1166
  623. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter17-orm.md +1584 -1584
  624. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter18-orm.md +1183 -1183
  625. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter19-orm.md +1016 -1016
  626. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter20-orm.md +1753 -1753
  627. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter21-orm.md +1447 -1447
  628. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter22-orm.md +1878 -1878
  629. package/lib/assets/docs/article/practical-database-design/part4/chapter22.md +965 -965
  630. package/lib/assets/docs/article/practical-database-design/part4/chapter23.md +2069 -2069
  631. package/lib/assets/docs/article/practical-database-design/part4/chapter24.md +2439 -2439
  632. package/lib/assets/docs/article/practical-database-design/part4/chapter25.md +3661 -3661
  633. package/lib/assets/docs/article/practical-database-design/part4/chapter26.md +2916 -2916
  634. package/lib/assets/docs/article/practical-database-design/part4/chapter27.md +3105 -3105
  635. package/lib/assets/docs/article/practical-database-design/part4/chapter28.md +2697 -2697
  636. package/lib/assets/docs/article/practical-database-design/part4/chapter29.md +2544 -2544
  637. package/lib/assets/docs/article/practical-database-design/part4/chapter30.md +2180 -2180
  638. package/lib/assets/docs/article/practical-database-design/part4/chapter31.md +1192 -1192
  639. package/lib/assets/docs/article/practical-database-design/part4/chapter32.md +2101 -2101
  640. package/lib/assets/docs/article/practical-database-design/part5/chapter33.md +1032 -1032
  641. package/lib/assets/docs/article/practical-database-design/part5/chapter34.md +1609 -1609
  642. package/lib/assets/docs/article/practical-database-design/part5/chapter35.md +1453 -1453
  643. package/lib/assets/docs/article/practical-database-design/part5/chapter36.md +1292 -1292
  644. package/lib/assets/docs/article/practical-database-design/part5/chapter37.md +1470 -1470
  645. package/lib/assets/docs/article/practical-database-design/part5/chapter38.md +1698 -1698
  646. package/lib/assets/docs/article/practical-database-design/part5/chapter39.md +2334 -2334
  647. package/lib/assets/docs/article/practical-database-design/study/study2-1.md +1693 -1693
  648. package/lib/assets/docs/article/practical-database-design/study/study2-2.md +1347 -1347
  649. package/lib/assets/docs/article/practical-database-design/study/study2-3.md +2044 -2044
  650. package/lib/assets/docs/article/practical-database-design/study/study2-4.md +2229 -2229
  651. package/lib/assets/docs/article/practical-database-design/study/study2-5.md +2418 -2418
  652. package/lib/assets/docs/article/practical-database-design/study/study3-1.md +2205 -2205
  653. package/lib/assets/docs/article/practical-database-design/study/study3-2.md +2221 -2221
  654. package/lib/assets/docs/article/practical-database-design/study/study3-3.md +2253 -2253
  655. package/lib/assets/docs/article/practical-database-design/study/study3-4.md +2106 -2106
  656. package/lib/assets/docs/article/practical-database-design/study/study3-5.md +2507 -2507
  657. package/lib/assets/docs/article/practical-database-design/study/study4-1.md +2587 -2587
  658. package/lib/assets/docs/article/practical-database-design/study/study4-2.md +2075 -2075
  659. package/lib/assets/docs/article/practical-database-design/study/study4-3.md +1805 -1805
  660. package/lib/assets/docs/article/practical-database-design/study/study4-4.md +1895 -1895
  661. package/lib/assets/docs/article/practical-database-design/study/study4-5.md +2878 -2878
  662. package/lib/assets/docs/assets/css/extra.css +29 -29
  663. package/lib/assets/docs/assets/js/extra.js +44 -44
  664. package/lib/assets/docs/design/index.md +44 -44
  665. package/lib/assets/docs/development/index.md +39 -39
  666. package/lib/assets/docs/index.md +33 -33
  667. package/lib/assets/docs/operation/index.md +27 -27
  668. package/lib/assets/docs/reference/CodexCLIMCP/343/202/242/343/203/227/343/203/252/343/202/261/343/203/274/343/202/267/343/203/247/343/203/263/351/226/213/347/231/272/343/203/225/343/203/255/343/203/274.md +546 -546
  669. package/lib/assets/docs/reference/CodexCLIMCP/343/202/265/343/203/274/343/203/220/343/203/274/350/250/255/345/256/232/346/211/213/351/240/206.md +341 -341
  670. package/lib/assets/docs/reference/Java/343/202/242/343/203/227/343/203/252/343/202/261/343/203/274/343/202/267/343/203/247/343/203/263/347/222/260/345/242/203/346/247/213/347/257/211/343/202/254/343/202/244/343/203/211.md +581 -581
  671. package/lib/assets/docs/reference/SonarQube/343/203/255/343/203/274/343/202/253/343/203/253/347/222/260/345/242/203/343/202/273/343/203/203/343/203/210/343/202/242/343/203/203/343/203/227/346/211/213/351/240/206/346/233/270.md +642 -642
  672. package/lib/assets/docs/reference/TypeScript/343/202/242/343/203/227/343/203/252/343/202/261/343/203/274/343/202/267/343/203/247/343/203/263/347/222/260/345/242/203/346/247/213/347/257/211/343/202/254/343/202/244/343/203/211.md +465 -465
  673. package/lib/assets/docs/reference/UI/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +450 -450
  674. package/lib/assets/docs/reference/images/Ansoff.drawio.svg +3 -3
  675. package/lib/assets/docs/reference/images/BMC.drawio.svg +3 -3
  676. package/lib/assets/docs/reference/images/BrandBasicStrategy.drawio.svg +3 -3
  677. package/lib/assets/docs/reference/images/BrandCategorization.drawio.svg +3 -3
  678. package/lib/assets/docs/reference/images/BrandRecurutementStrategy.drawio.svg +3 -3
  679. package/lib/assets/docs/reference/images/BrandValue.drawio.svg +3 -3
  680. package/lib/assets/docs/reference/images/BusinessActivitiy.svg +3 -3
  681. package/lib/assets/docs/reference/images/HRM.drawio.svg +3 -3
  682. package/lib/assets/docs/reference/images/MarketingStructure.drawio.svg +3 -3
  683. package/lib/assets/docs/reference/images/OrganizationElemnts.svg +3 -3
  684. package/lib/assets/docs/reference/images/PPM.drawio.svg +3 -3
  685. package/lib/assets/docs/reference/images/PositioningMap.drawio.svg +3 -3
  686. package/lib/assets/docs/reference/images/ProductLayer.drawio.svg +3 -3
  687. package/lib/assets/docs/reference/images/ProductMix.drawio.svg +3 -3
  688. package/lib/assets/docs/reference/images/SWOT.drawio.svg +3 -3
  689. package/lib/assets/docs/reference/images/TargetMarket.drawio.svg +3 -3
  690. package/lib/assets/docs/reference/images/ThreeGenericStrategies.drawio.svg +3 -3
  691. package/lib/assets/docs/reference/images/VRIO.drawio.svg +3 -3
  692. package/lib/assets/docs/reference/images/ValueChain.drawio.svg +3 -3
  693. package/lib/assets/docs/reference/index.md +53 -53
  694. package/lib/assets/docs/reference//343/202/210/343/201/204/343/202/275/343/203/225/343/203/210/343/202/246/343/202/247/343/202/242/343/201/250/343/201/257.md +250 -250
  695. package/lib/assets/docs/reference//343/202/242/343/203/274/343/202/255/343/203/206/343/202/257/343/203/201/343/203/243/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +2216 -2216
  696. package/lib/assets/docs/reference//343/202/244/343/203/263/343/203/225/343/203/251/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +1878 -1878
  697. package/lib/assets/docs/reference//343/202/250/343/202/257/343/202/271/343/203/210/343/203/252/343/203/274/343/203/240/343/203/227/343/203/255/343/202/260/343/203/251/343/203/237/343/203/263/343/202/260.md +550 -550
  698. package/lib/assets/docs/reference//343/202/263/343/203/274/343/203/207/343/202/243/343/203/263/343/202/260/343/201/250/343/203/206/343/202/271/343/203/210/343/202/254/343/202/244/343/203/211.md +705 -705
  699. package/lib/assets/docs/reference//343/203/206/343/202/271/343/203/210/346/210/246/347/225/245/343/202/254/343/202/244/343/203/211.md +1313 -1313
  700. package/lib/assets/docs/reference//343/203/207/343/203/274/343/202/277/343/203/242/343/203/207/343/203/253/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +311 -311
  701. package/lib/assets/docs/reference//343/203/211/343/203/241/343/202/244/343/203/263/343/203/242/343/203/207/343/203/253/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +599 -599
  702. package/lib/assets/docs/reference//343/203/223/343/202/270/343/203/215/343/202/271/343/202/242/343/203/274/343/202/255/343/203/206/343/202/257/343/203/201/343/203/243/345/210/206/346/236/220/343/202/254/343/202/244/343/203/211.md +528 -528
  703. package/lib/assets/docs/reference//343/203/246/343/203/274/343/202/271/343/202/261/343/203/274/343/202/271/344/275/234/346/210/220/343/202/254/343/202/244/343/203/211.md +689 -689
  704. package/lib/assets/docs/reference//343/203/252/343/203/252/343/203/274/343/202/271/343/202/254/343/202/244/343/203/211.md +461 -461
  705. package/lib/assets/docs/reference//343/203/252/343/203/252/343/203/274/343/202/271/343/203/273/343/202/244/343/203/206/343/203/254/343/203/274/343/202/267/343/203/247/343/203/263/350/250/210/347/224/273/343/202/254/343/202/244/343/203/211.md +580 -580
  706. package/lib/assets/docs/reference//343/203/255/343/202/270/343/202/253/343/203/253/343/202/267/343/203/263/343/202/255/343/203/263/343/202/260.md +1367 -1367
  707. package/lib/assets/docs/reference//344/274/201/346/245/255/347/265/214/345/226/266/350/253/226.md +2637 -2637
  708. package/lib/assets/docs/reference//347/222/260/345/242/203/345/244/211/346/225/260/347/256/241/347/220/206/343/202/254/343/202/244/343/203/211.md +665 -665
  709. package/lib/assets/docs/reference//347/265/214/345/226/266/346/210/246/347/225/245/345/210/206/346/236/220/343/202/254/343/202/244/343/203/211.md +566 -566
  710. package/lib/assets/docs/reference//350/246/201/344/273/266/345/256/232/347/276/251/343/202/254/343/202/244/343/203/211.md +1248 -1248
  711. package/lib/assets/docs/reference//350/250/200/350/252/236/345/210/245/351/226/213/347/231/272/343/202/254/343/202/244/343/203/211.md +518 -518
  712. package/lib/assets/docs/reference//351/201/213/345/226/266/347/256/241/347/220/206.md +1482 -1482
  713. package/lib/assets/docs/reference//351/201/213/347/224/250/343/202/271/343/202/257/343/203/252/343/203/227/343/203/210/344/275/234/346/210/220/343/202/254/343/202/244/343/203/211.md +421 -421
  714. package/lib/assets/docs/reference//351/201/213/347/224/250/350/246/201/344/273/266/345/256/232/347/276/251/343/202/254/343/202/244/343/203/211.md +392 -392
  715. package/lib/assets/docs/reference//351/226/213/347/231/272/343/202/254/343/202/244/343/203/211.md +305 -305
  716. package/lib/assets/docs/reference//351/235/236/346/251/237/350/203/275/350/246/201/344/273/266/345/256/232/347/276/251/343/202/254/343/202/244/343/203/211.md +1236 -1236
  717. package/lib/assets/docs/requirements/index.md +17 -17
  718. package/lib/assets/docs/review/index.md +5 -5
  719. package/lib/assets/docs/strategy/index.md +17 -17
  720. package/lib/assets/docs/template/ADR.md +30 -30
  721. package/lib/assets/docs/template/AWS/343/202/271/343/203/206/343/203/274/343/202/270/343/203/263/343/202/260/347/222/260/345/242/203/343/202/273/343/203/203/343/203/210/343/202/242/343/203/203/343/203/227/346/211/213/351/240/206/346/233/270.md +1366 -1366
  722. package/lib/assets/docs/template/AWS/343/203/227/343/203/255/343/203/200/343/202/257/343/202/267/343/203/247/343/203/263/347/222/260/345/242/203/343/202/273/343/203/203/343/203/210/343/202/242/343/203/203/343/203/227/346/211/213/351/240/206/346/233/270.md +634 -634
  723. package/lib/assets/docs/template/README.md +50 -50
  724. package/lib/assets/docs/template/index.md +24 -24
  725. package/lib/assets/docs/template//343/201/276/343/201/232/343/201/223/343/202/214/343/202/222/350/252/255/343/202/202/343/201/206/343/203/252/343/202/271/343/203/210.md +12 -12
  726. package/lib/assets/docs/template//343/202/242/343/203/227/343/203/252/343/202/261/343/203/274/343/202/267/343/203/247/343/203/263/351/226/213/347/231/272/347/222/260/345/242/203/343/202/273/343/203/203/343/203/210/343/202/242/343/203/203/343/203/227/346/211/213/351/240/206/346/233/270.md +547 -547
  727. package/lib/assets/docs/template//343/202/244/343/203/206/343/203/254/343/203/274/343/202/267/343/203/247/343/203/263/345/256/214/344/272/206/345/240/261/345/221/212/346/233/270.md +58 -58
  728. package/lib/assets/docs/template//343/202/244/343/203/206/343/203/254/343/203/274/343/202/267/343/203/247/343/203/263/350/250/210/347/224/273.md +327 -327
  729. package/lib/assets/docs/template//343/202/244/343/203/263/343/202/273/343/203/227/343/202/267/343/203/247/343/203/263/343/203/207/343/203/203/343/202/255.md +13 -13
  730. package/lib/assets/docs/template//343/203/223/343/202/270/343/203/215/343/202/271/343/202/242/343/203/274/343/202/255/343/203/206/343/202/257/343/203/201/343/203/243.md +379 -379
  731. package/lib/assets/docs/template//343/203/252/343/203/252/343/203/274/343/202/271/345/256/214/344/272/206/345/240/261/345/221/212/346/233/270.md +275 -275
  732. package/lib/assets/docs/template//344/272/213/344/276/213/345/210/206/346/236/220.md +513 -513
  733. package/lib/assets/docs/template//344/274/201/346/245/255/345/210/206/346/236/220.md +573 -573
  734. package/lib/assets/docs/template//345/256/214/345/205/250/345/275/242/345/274/217/343/201/256/343/203/246/343/203/274/343/202/271/343/202/261/343/203/274/343/202/271.md +69 -69
  735. package/lib/assets/docs/template//350/246/201/344/273/266/345/256/232/347/276/251.md +669 -669
  736. package/lib/assets/docs/template//350/250/255/350/250/210.md +173 -173
  737. package/lib/assets/docs/template//351/226/213/347/231/272/347/222/260/345/242/203/343/202/273/343/203/203/343/203/210/343/202/242/343/203/203/343/203/227/346/211/213/351/240/206/346/233/270.md +688 -688
  738. package/lib/assets/gulpfile.js +27 -25
  739. package/lib/assets/mkdocs.yml +139 -139
  740. package/lib/assets/ops/docker/mkdoc/Dockerfile +19 -19
  741. package/lib/assets/ops/docker/sonarqube-local/docker-compose.yml +57 -57
  742. package/lib/assets/ops/scripts/journal.js +180 -180
  743. package/lib/assets/ops/scripts/manual.js +198 -0
  744. package/lib/assets/ops/scripts/mkdocs.js +82 -82
  745. package/lib/assets/ops/scripts/release.js +431 -431
  746. package/lib/assets/ops/scripts/sonar_local.js +726 -726
  747. package/lib/assets/ops/scripts/ssh.js +190 -190
  748. package/lib/assets/ops/scripts/vault.js +299 -299
  749. package/lib/assets/package-lock.json +1673 -1653
  750. package/lib/assets/package.json +43 -40
  751. package/lib/gulpfile.js +37 -37
  752. package/package.json +41 -41
@@ -1,1698 +1,1698 @@
1
- # 第38章:API 設計とサービス連携
2
-
3
- 本章では、基幹業務システムにおける API 設計の原則と、サービス間連携のパターンについて解説します。RESTful API の設計、サービス間通信の方式、API ゲートウェイの活用、そしてインテグレーションテストの実践方法を学びます。
4
-
5
- ---
6
-
7
- ## 38.1 API 設計の原則
8
-
9
- ### RESTful API の設計
10
-
11
- REST(Representational State Transfer)は、Web API 設計の標準的なアーキテクチャスタイルです。
12
-
13
- ```plantuml
14
- @startuml
15
- title RESTful API の基本原則
16
-
17
- rectangle "REST の制約" as constraints {
18
- rectangle "クライアント-サーバー" as cs {
19
- note right
20
- 関心の分離
21
- 独立した進化
22
- end note
23
- }
24
-
25
- rectangle "ステートレス" as stateless {
26
- note right
27
- 各リクエストは独立
28
- セッション状態を保持しない
29
- end note
30
- }
31
-
32
- rectangle "キャッシュ可能" as cacheable {
33
- note right
34
- レスポンスにキャッシュ可否を明示
35
- パフォーマンス向上
36
- end note
37
- }
38
-
39
- rectangle "統一インターフェース" as uniform {
40
- note right
41
- リソース識別(URI)
42
- 表現によるリソース操作
43
- 自己記述メッセージ
44
- HATEOAS
45
- end note
46
- }
47
-
48
- rectangle "階層化システム" as layered {
49
- note right
50
- 中間サーバーの追加が可能
51
- ロードバランサー、キャッシュ等
52
- end note
53
- }
54
- }
55
-
56
- @enduml
57
- ```
58
-
59
- #### HTTP メソッドとリソース操作
60
-
61
- | HTTP メソッド | 操作 | 冪等性 | 安全性 | 使用例 |
62
- |-------------|-----|-------|-------|-------|
63
- | GET | 取得 | Yes | Yes | リソースの参照 |
64
- | POST | 作成 | No | No | 新規リソース作成 |
65
- | PUT | 置換 | Yes | No | リソース全体の更新 |
66
- | PATCH | 部分更新 | No | No | リソースの一部更新 |
67
- | DELETE | 削除 | Yes | No | リソースの削除 |
68
-
69
- ### リソース指向設計
70
-
71
- API はリソース(名詞)を中心に設計し、操作は HTTP メソッドで表現します。
72
-
73
- ```plantuml
74
- @startuml
75
- title リソース指向 API 設計
76
-
77
- package "販売管理 API" as sales_api {
78
- rectangle "/orders" as orders {
79
- rectangle "GET /orders" as get_orders
80
- rectangle "POST /orders" as post_order
81
- rectangle "GET /orders/{id}" as get_order
82
- rectangle "PUT /orders/{id}" as put_order
83
- rectangle "DELETE /orders/{id}" as delete_order
84
- }
85
-
86
- rectangle "/orders/{id}/lines" as order_lines {
87
- rectangle "GET /orders/{id}/lines" as get_lines
88
- rectangle "POST /orders/{id}/lines" as post_line
89
- }
90
-
91
- rectangle "/customers" as customers {
92
- rectangle "GET /customers" as get_customers
93
- rectangle "POST /customers" as post_customer
94
- rectangle "GET /customers/{id}" as get_customer
95
- }
96
-
97
- rectangle "/customers/{id}/orders" as customer_orders {
98
- rectangle "GET /customers/{id}/orders" as get_customer_orders
99
- }
100
- }
101
-
102
- note bottom of sales_api
103
- 【設計原則】
104
- ・リソースは名詞(複数形)
105
- ・操作はHTTPメソッドで表現
106
- ・階層構造で関連を表現
107
- ・クエリパラメータでフィルタリング
108
- end note
109
-
110
- @enduml
111
- ```
112
-
113
- #### 基幹業務システムの API エンドポイント設計
114
-
115
- ```plantuml
116
- @startuml
117
- title 基幹業務システム API エンドポイント
118
-
119
- package "販売管理 API" as sales {
120
- rectangle "/api/v1/sales" as sales_base {
121
- rectangle "受注: /orders"
122
- rectangle "出荷: /shipments"
123
- rectangle "売上: /sales"
124
- rectangle "請求: /invoices"
125
- rectangle "入金: /payments"
126
- }
127
- rectangle "/api/v1/sales/masters" as sales_masters {
128
- rectangle "顧客: /customers"
129
- rectangle "商品: /products"
130
- }
131
- }
132
-
133
- package "財務会計 API" as accounting {
134
- rectangle "/api/v1/accounting" as acc_base {
135
- rectangle "仕訳: /journals"
136
- rectangle "残高: /balances"
137
- rectangle "試算表: /trial-balances"
138
- }
139
- rectangle "/api/v1/accounting/masters" as acc_masters {
140
- rectangle "勘定科目: /accounts"
141
- rectangle "補助科目: /sub-accounts"
142
- }
143
- }
144
-
145
- package "生産管理 API" as production {
146
- rectangle "/api/v1/production" as prod_base {
147
- rectangle "製造指示: /work-orders"
148
- rectangle "発注: /purchase-orders"
149
- rectangle "在庫: /inventory"
150
- }
151
- rectangle "/api/v1/production/masters" as prod_masters {
152
- rectangle "品目: /items"
153
- rectangle "BOM: /bom"
154
- rectangle "工程: /processes"
155
- }
156
- }
157
-
158
- @enduml
159
- ```
160
-
161
- <details>
162
- <summary>OpenAPI 定義例</summary>
163
-
164
- ```yaml
165
- openapi: 3.0.3
166
- info:
167
- title: 販売管理 API
168
- version: 1.0.0
169
- description: 基幹業務システム - 販売管理 API
170
-
171
- servers:
172
- - url: https://api.example.com/api/v1/sales
173
- description: Production server
174
-
175
- paths:
176
- /orders:
177
- get:
178
- summary: 受注一覧取得
179
- operationId: getOrders
180
- parameters:
181
- - name: customerId
182
- in: query
183
- schema:
184
- type: string
185
- - name: status
186
- in: query
187
- schema:
188
- type: string
189
- enum: [DRAFT, CONFIRMED, SHIPPED, COMPLETED]
190
- - name: fromDate
191
- in: query
192
- schema:
193
- type: string
194
- format: date
195
- - name: toDate
196
- in: query
197
- schema:
198
- type: string
199
- format: date
200
- - name: page
201
- in: query
202
- schema:
203
- type: integer
204
- default: 0
205
- - name: size
206
- in: query
207
- schema:
208
- type: integer
209
- default: 20
210
- responses:
211
- '200':
212
- description: 成功
213
- content:
214
- application/json:
215
- schema:
216
- $ref: '#/components/schemas/OrderListResponse'
217
-
218
- post:
219
- summary: 受注登録
220
- operationId: createOrder
221
- requestBody:
222
- required: true
223
- content:
224
- application/json:
225
- schema:
226
- $ref: '#/components/schemas/CreateOrderRequest'
227
- responses:
228
- '201':
229
- description: 作成成功
230
- content:
231
- application/json:
232
- schema:
233
- $ref: '#/components/schemas/OrderResponse'
234
- '400':
235
- description: バリデーションエラー
236
- content:
237
- application/problem+json:
238
- schema:
239
- $ref: '#/components/schemas/ProblemDetail'
240
-
241
- /orders/{orderId}:
242
- get:
243
- summary: 受注詳細取得
244
- operationId: getOrder
245
- parameters:
246
- - name: orderId
247
- in: path
248
- required: true
249
- schema:
250
- type: string
251
- responses:
252
- '200':
253
- description: 成功
254
- content:
255
- application/json:
256
- schema:
257
- $ref: '#/components/schemas/OrderResponse'
258
- '404':
259
- description: 受注が見つからない
260
-
261
- components:
262
- schemas:
263
- OrderResponse:
264
- type: object
265
- properties:
266
- orderId:
267
- type: string
268
- customerId:
269
- type: string
270
- customerName:
271
- type: string
272
- orderDate:
273
- type: string
274
- format: date
275
- status:
276
- type: string
277
- enum: [DRAFT, CONFIRMED, SHIPPED, COMPLETED]
278
- totalAmount:
279
- type: number
280
- taxAmount:
281
- type: number
282
- lines:
283
- type: array
284
- items:
285
- $ref: '#/components/schemas/OrderLineResponse'
286
- _links:
287
- $ref: '#/components/schemas/Links'
288
-
289
- CreateOrderRequest:
290
- type: object
291
- required:
292
- - customerId
293
- - orderDate
294
- - lines
295
- properties:
296
- customerId:
297
- type: string
298
- orderDate:
299
- type: string
300
- format: date
301
- requestedDeliveryDate:
302
- type: string
303
- format: date
304
- lines:
305
- type: array
306
- items:
307
- $ref: '#/components/schemas/CreateOrderLineRequest'
308
- minItems: 1
309
-
310
- ProblemDetail:
311
- type: object
312
- properties:
313
- type:
314
- type: string
315
- title:
316
- type: string
317
- status:
318
- type: integer
319
- detail:
320
- type: string
321
- instance:
322
- type: string
323
- ```
324
-
325
- </details>
326
-
327
- ### バージョニング戦略
328
-
329
- API の互換性を維持しながら進化させるためのバージョニング戦略を検討します。
330
-
331
- ```plantuml
332
- @startuml
333
- title API バージョニング戦略の比較
334
-
335
- rectangle "URI パスバージョニング" as uri {
336
- note right
337
- /api/v1/orders
338
- /api/v2/orders
339
-
340
- 【メリット】
341
- ・明確で分かりやすい
342
- ・キャッシュしやすい
343
-
344
- 【デメリット】
345
- ・URIの変更が必要
346
- end note
347
- }
348
-
349
- rectangle "クエリパラメータ" as query {
350
- note right
351
- /api/orders?version=1
352
- /api/orders?version=2
353
-
354
- 【メリット】
355
- ・URIが変わらない
356
-
357
- 【デメリット】
358
- ・見落としやすい
359
- end note
360
- }
361
-
362
- rectangle "カスタムヘッダー" as header {
363
- note right
364
- X-API-Version: 1
365
- X-API-Version: 2
366
-
367
- 【メリット】
368
- ・URIがクリーン
369
-
370
- 【デメリット】
371
- ・テストしにくい
372
- end note
373
- }
374
-
375
- rectangle "Accept ヘッダー" as accept {
376
- note right
377
- Accept: application/vnd.company.v1+json
378
- Accept: application/vnd.company.v2+json
379
-
380
- 【メリット】
381
- ・HTTP標準に準拠
382
-
383
- 【デメリット】
384
- ・複雑
385
- end note
386
- }
387
-
388
- @enduml
389
- ```
390
-
391
- #### 推奨:URI パスバージョニング
392
-
393
- ```plantuml
394
- @startuml
395
- title URI パスバージョニングの運用
396
-
397
- rectangle "v1(現行)" as v1 {
398
- rectangle "/api/v1/orders" as v1_orders
399
- rectangle "/api/v1/customers" as v1_customers
400
- }
401
-
402
- rectangle "v2(新版)" as v2 {
403
- rectangle "/api/v2/orders" as v2_orders
404
- rectangle "/api/v2/customers" as v2_customers
405
- }
406
-
407
- note bottom of v1
408
- 【v1 → v2 移行】
409
- 1. v2を新規リリース
410
- 2. 並行運用期間
411
- 3. v1を非推奨化(Deprecation)
412
- 4. v1を廃止(Sunset)
413
-
414
- 【Deprecation ヘッダー】
415
- Deprecation: true
416
- Sunset: Sat, 01 Jan 2025 00:00:00 GMT
417
- end note
418
-
419
- @enduml
420
- ```
421
-
422
- ---
423
-
424
- ## 38.2 サービス間通信
425
-
426
- ### 同期通信(REST / gRPC)
427
-
428
- ```plantuml
429
- @startuml
430
- title 同期通信パターンの比較
431
-
432
- rectangle "REST over HTTP" as rest {
433
- rectangle "販売サービス" as sales_rest
434
- rectangle "在庫サービス" as inv_rest
435
-
436
- sales_rest --> inv_rest : HTTP/JSON
437
- }
438
-
439
- rectangle "gRPC" as grpc {
440
- rectangle "販売サービス" as sales_grpc
441
- rectangle "在庫サービス" as inv_grpc
442
-
443
- sales_grpc --> inv_grpc : HTTP/2 + Protocol Buffers
444
- }
445
-
446
- note bottom of rest
447
- 【REST】
448
- ・テキストベース(JSON)
449
- ・ブラウザから直接呼び出し可能
450
- ・広く普及、ツール豊富
451
-
452
- 【適用場面】
453
- ・外部公開API
454
- ・フロントエンド連携
455
- end note
456
-
457
- note bottom of grpc
458
- 【gRPC】
459
- ・バイナリプロトコル
460
- ・高速、低レイテンシ
461
- ・スキーマ定義必須
462
-
463
- 【適用場面】
464
- ・マイクロサービス間
465
- ・高性能要件
466
- end note
467
-
468
- @enduml
469
- ```
470
-
471
- <details>
472
- <summary>gRPC 定義例</summary>
473
-
474
- ```protobuf
475
- syntax = "proto3";
476
-
477
- package sales.v1;
478
-
479
- option java_package = "com.example.sales.grpc";
480
- option java_multiple_files = true;
481
-
482
- // 在庫サービス
483
- service InventoryService {
484
- // 在庫照会
485
- rpc GetStock(GetStockRequest) returns (StockResponse);
486
-
487
- // 在庫引当
488
- rpc AllocateStock(AllocateStockRequest) returns (AllocateStockResponse);
489
-
490
- // 引当解除
491
- rpc ReleaseStock(ReleaseStockRequest) returns (ReleaseStockResponse);
492
- }
493
-
494
- message GetStockRequest {
495
- string product_id = 1;
496
- string warehouse_id = 2;
497
- }
498
-
499
- message StockResponse {
500
- string product_id = 1;
501
- string warehouse_id = 2;
502
- int32 quantity = 3;
503
- int32 allocated_quantity = 4;
504
- int32 available_quantity = 5;
505
- }
506
-
507
- message AllocateStockRequest {
508
- string product_id = 1;
509
- string warehouse_id = 2;
510
- int32 quantity = 3;
511
- string order_id = 4;
512
- }
513
-
514
- message AllocateStockResponse {
515
- bool success = 1;
516
- string allocation_id = 2;
517
- string error_message = 3;
518
- }
519
- ```
520
-
521
- </details>
522
-
523
- ### 非同期通信(メッセージキュー)
524
-
525
- ```plantuml
526
- @startuml
527
- title 非同期通信パターン
528
-
529
- rectangle "販売サービス" as sales
530
-
531
- rectangle "メッセージブローカー" as broker {
532
- queue "受注キュー" as order_queue
533
- collections "売上トピック" as sales_topic
534
- }
535
-
536
- rectangle "在庫サービス" as inventory
537
- rectangle "会計サービス" as accounting
538
- rectangle "通知サービス" as notification
539
-
540
- sales --> order_queue : 受注メッセージ
541
- order_queue --> inventory : 在庫引当
542
-
543
- sales --> sales_topic : 売上イベント
544
- sales_topic --> accounting : 仕訳生成
545
- sales_topic --> notification : 通知送信
546
- sales_topic --> inventory : 在庫更新
547
-
548
- note bottom of broker
549
- 【Point-to-Point】
550
- ・1対1の通信
551
- ・キューで実現
552
- ・負荷分散可能
553
-
554
- 【Publish-Subscribe】
555
- ・1対多の通信
556
- ・トピックで実現
557
- ・イベント配信
558
- end note
559
-
560
- @enduml
561
- ```
562
-
563
- <details>
564
- <summary>Java 実装例(Spring AMQP)</summary>
565
-
566
- ```java
567
- // メッセージ送信
568
- @Service
569
- public class OrderMessagePublisher {
570
- private final RabbitTemplate rabbitTemplate;
571
-
572
- public void publishOrderCreated(OrderCreatedEvent event) {
573
- rabbitTemplate.convertAndSend(
574
- "sales.exchange",
575
- "order.created",
576
- event
577
- );
578
- }
579
-
580
- public void publishSalesCompleted(SalesCompletedEvent event) {
581
- rabbitTemplate.convertAndSend(
582
- "sales.topic.exchange",
583
- "sales.completed",
584
- event
585
- );
586
- }
587
- }
588
-
589
- // メッセージ受信
590
- @Component
591
- public class OrderMessageListener {
592
-
593
- @RabbitListener(queues = "inventory.order.queue")
594
- public void handleOrderCreated(OrderCreatedEvent event) {
595
- // 在庫引当処理
596
- log.info("Received order: {}", event.orderId());
597
- inventoryService.allocate(event);
598
- }
599
- }
600
-
601
- // 設定
602
- @Configuration
603
- public class RabbitMQConfig {
604
-
605
- @Bean
606
- public TopicExchange salesTopicExchange() {
607
- return new TopicExchange("sales.topic.exchange");
608
- }
609
-
610
- @Bean
611
- public Queue accountingQueue() {
612
- return new Queue("accounting.sales.queue", true);
613
- }
614
-
615
- @Bean
616
- public Binding accountingBinding(Queue accountingQueue,
617
- TopicExchange salesTopicExchange) {
618
- return BindingBuilder.bind(accountingQueue)
619
- .to(salesTopicExchange)
620
- .with("sales.*");
621
- }
622
- }
623
- ```
624
-
625
- </details>
626
-
627
- ### サーキットブレーカーパターン
628
-
629
- サービス間通信の障害を検知し、障害の連鎖を防ぐパターンです。
630
-
631
- ```plantuml
632
- @startuml
633
- title サーキットブレーカーの状態遷移
634
-
635
- [*] --> Closed : 初期状態
636
-
637
- Closed --> Open : 失敗が閾値を超過
638
- Closed --> Closed : 成功
639
-
640
- Open --> HalfOpen : タイムアウト後
641
- Open --> Open : リクエスト拒否
642
-
643
- HalfOpen --> Closed : 試行成功
644
- HalfOpen --> Open : 試行失敗
645
-
646
- note right of Closed
647
- 【Closed】
648
- 通常稼働
649
- リクエストを通す
650
- end note
651
-
652
- note right of Open
653
- 【Open】
654
- 障害検知
655
- 即座にエラー返却
656
- (Fail Fast)
657
- end note
658
-
659
- note right of HalfOpen
660
- 【Half-Open】
661
- 復旧確認中
662
- 限定的に試行
663
- end note
664
-
665
- @enduml
666
- ```
667
-
668
- ```plantuml
669
- @startuml
670
- title サーキットブレーカーの動作フロー
671
-
672
- |クライアント|
673
- start
674
- :サービス呼び出し;
675
-
676
- |サーキットブレーカー|
677
- if (回路状態?) then (Open)
678
- :即座にフォールバック;
679
- |クライアント|
680
- :フォールバック結果;
681
- stop
682
- else (Closed/HalfOpen)
683
- endif
684
-
685
- :下流サービス呼び出し;
686
-
687
- |下流サービス|
688
- if (処理成功?) then (yes)
689
- :正常レスポンス;
690
- |サーキットブレーカー|
691
- :成功をカウント;
692
- :Closedを維持/遷移;
693
- else (no)
694
- :エラー/タイムアウト;
695
- |サーキットブレーカー|
696
- :失敗をカウント;
697
-
698
- if (失敗数 >= 閾値?) then (yes)
699
- :Openに遷移;
700
- endif
701
- endif
702
-
703
- |クライアント|
704
- :結果を返却;
705
-
706
- stop
707
-
708
- @enduml
709
- ```
710
-
711
- <details>
712
- <summary>Java 実装例(Resilience4j)</summary>
713
-
714
- ```java
715
- // 設定
716
- @Configuration
717
- public class CircuitBreakerConfig {
718
-
719
- @Bean
720
- public CircuitBreakerRegistry circuitBreakerRegistry() {
721
- CircuitBreakerConfig config = CircuitBreakerConfig.custom()
722
- .failureRateThreshold(50) // 失敗率50%でOpen
723
- .waitDurationInOpenState(Duration.ofSeconds(30))
724
- .permittedNumberOfCallsInHalfOpenState(3)
725
- .slidingWindowType(SlidingWindowType.COUNT_BASED)
726
- .slidingWindowSize(10)
727
- .build();
728
-
729
- return CircuitBreakerRegistry.of(config);
730
- }
731
- }
732
-
733
- // サービス
734
- @Service
735
- public class InventoryServiceClient {
736
- private final RestTemplate restTemplate;
737
- private final CircuitBreaker circuitBreaker;
738
-
739
- public InventoryServiceClient(RestTemplate restTemplate,
740
- CircuitBreakerRegistry registry) {
741
- this.restTemplate = restTemplate;
742
- this.circuitBreaker = registry.circuitBreaker("inventoryService");
743
- }
744
-
745
- public StockResponse getStock(String productId) {
746
- Supplier<StockResponse> supplier = CircuitBreaker
747
- .decorateSupplier(circuitBreaker, () -> {
748
- return restTemplate.getForObject(
749
- "/api/v1/inventory/{productId}",
750
- StockResponse.class,
751
- productId
752
- );
753
- });
754
-
755
- return Try.ofSupplier(supplier)
756
- .recover(CallNotPermittedException.class,
757
- e -> getFallbackStock(productId))
758
- .recover(Exception.class,
759
- e -> getFallbackStock(productId))
760
- .get();
761
- }
762
-
763
- private StockResponse getFallbackStock(String productId) {
764
- // フォールバック: キャッシュから取得または推定値
765
- return new StockResponse(productId, 0, 0, 0, "UNKNOWN");
766
- }
767
- }
768
-
769
- // アノテーションベース
770
- @Service
771
- public class AccountingServiceClient {
772
-
773
- @CircuitBreaker(name = "accountingService", fallbackMethod = "fallback")
774
- @Retry(name = "accountingService")
775
- @TimeLimiter(name = "accountingService")
776
- public CompletableFuture<JournalResponse> createJournal(
777
- CreateJournalRequest request) {
778
- return CompletableFuture.supplyAsync(() -> {
779
- return restTemplate.postForObject(
780
- "/api/v1/accounting/journals",
781
- request,
782
- JournalResponse.class
783
- );
784
- });
785
- }
786
-
787
- public CompletableFuture<JournalResponse> fallback(
788
- CreateJournalRequest request, Throwable t) {
789
- log.warn("Fallback for createJournal: {}", t.getMessage());
790
- // 仕訳をキューに保存して後で再試行
791
- pendingJournalQueue.add(request);
792
- return CompletableFuture.completedFuture(
793
- JournalResponse.pending(request.getCorrelationId())
794
- );
795
- }
796
- }
797
- ```
798
-
799
- </details>
800
-
801
- ---
802
-
803
- ## 38.3 API ゲートウェイ
804
-
805
- API ゲートウェイは、クライアントと複数のバックエンドサービスの間に位置し、横断的な関心事を一元的に処理します。
806
-
807
- ```plantuml
808
- @startuml
809
- title API ゲートウェイアーキテクチャ
810
-
811
- rectangle "クライアント" as clients {
812
- rectangle "Webアプリ" as web
813
- rectangle "モバイルアプリ" as mobile
814
- rectangle "外部システム" as external
815
- }
816
-
817
- rectangle "API Gateway" as gateway {
818
- rectangle "認証・認可" as auth
819
- rectangle "レート制限" as rate
820
- rectangle "ルーティング" as routing
821
- rectangle "ログ・監視" as logging
822
- rectangle "変換・集約" as transform
823
- }
824
-
825
- rectangle "バックエンドサービス" as backend {
826
- rectangle "販売API" as sales
827
- rectangle "会計API" as accounting
828
- rectangle "生産API" as production
829
- }
830
-
831
- web --> gateway
832
- mobile --> gateway
833
- external --> gateway
834
-
835
- gateway --> sales
836
- gateway --> accounting
837
- gateway --> production
838
-
839
- note bottom of gateway
840
- 【API Gatewayの責務】
841
- ・認証・認可の一元化
842
- ・レート制限
843
- ・リクエストルーティング
844
- ・ログ集約
845
- ・レスポンス変換
846
- ・API集約
847
- end note
848
-
849
- @enduml
850
- ```
851
-
852
- ### 認証・認可の一元化
853
-
854
- ```plantuml
855
- @startuml
856
- title JWT 認証フロー
857
-
858
- |クライアント|
859
- start
860
- :認証リクエスト;
861
- note right
862
- POST /auth/login
863
- {username, password}
864
- end note
865
-
866
- |認証サービス|
867
- :認証情報検証;
868
- :JWTトークン発行;
869
- note right
870
- Header: {alg, typ}
871
- Payload: {sub, roles, exp}
872
- Signature
873
- end note
874
-
875
- |クライアント|
876
- :JWTトークン保存;
877
- :API呼び出し;
878
- note right
879
- Authorization: Bearer {token}
880
- end note
881
-
882
- |API Gateway|
883
- :JWTトークン検証;
884
- if (有効?) then (yes)
885
- :クレーム抽出;
886
- :認可チェック;
887
- if (権限あり?) then (yes)
888
- |バックエンドサービス|
889
- :リクエスト処理;
890
- :レスポンス返却;
891
- else (no)
892
- :403 Forbidden;
893
- endif
894
- else (no)
895
- :401 Unauthorized;
896
- endif
897
-
898
- |クライアント|
899
- :結果受信;
900
-
901
- stop
902
-
903
- @enduml
904
- ```
905
-
906
- <details>
907
- <summary>Java 実装例(Spring Security + JWT)</summary>
908
-
909
- ```java
910
- // JWT フィルター
911
- @Component
912
- public class JwtAuthenticationFilter extends OncePerRequestFilter {
913
- private final JwtTokenProvider tokenProvider;
914
-
915
- @Override
916
- protected void doFilterInternal(HttpServletRequest request,
917
- HttpServletResponse response,
918
- FilterChain filterChain)
919
- throws ServletException, IOException {
920
-
921
- String token = extractToken(request);
922
-
923
- if (token != null && tokenProvider.validateToken(token)) {
924
- Authentication auth = tokenProvider.getAuthentication(token);
925
- SecurityContextHolder.getContext().setAuthentication(auth);
926
- }
927
-
928
- filterChain.doFilter(request, response);
929
- }
930
-
931
- private String extractToken(HttpServletRequest request) {
932
- String bearerToken = request.getHeader("Authorization");
933
- if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
934
- return bearerToken.substring(7);
935
- }
936
- return null;
937
- }
938
- }
939
-
940
- // JWT プロバイダー
941
- @Component
942
- public class JwtTokenProvider {
943
- @Value("${jwt.secret}")
944
- private String jwtSecret;
945
-
946
- @Value("${jwt.expiration}")
947
- private long jwtExpiration;
948
-
949
- public String generateToken(Authentication authentication) {
950
- UserDetails userDetails = (UserDetails) authentication.getPrincipal();
951
-
952
- return Jwts.builder()
953
- .setSubject(userDetails.getUsername())
954
- .claim("roles", userDetails.getAuthorities().stream()
955
- .map(GrantedAuthority::getAuthority)
956
- .collect(Collectors.toList()))
957
- .setIssuedAt(new Date())
958
- .setExpiration(new Date(System.currentTimeMillis() + jwtExpiration))
959
- .signWith(SignatureAlgorithm.HS512, jwtSecret)
960
- .compact();
961
- }
962
-
963
- public boolean validateToken(String token) {
964
- try {
965
- Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token);
966
- return true;
967
- } catch (JwtException | IllegalArgumentException e) {
968
- return false;
969
- }
970
- }
971
-
972
- public Authentication getAuthentication(String token) {
973
- Claims claims = Jwts.parser()
974
- .setSigningKey(jwtSecret)
975
- .parseClaimsJws(token)
976
- .getBody();
977
-
978
- List<String> roles = claims.get("roles", List.class);
979
- List<GrantedAuthority> authorities = roles.stream()
980
- .map(SimpleGrantedAuthority::new)
981
- .collect(Collectors.toList());
982
-
983
- User principal = new User(claims.getSubject(), "", authorities);
984
- return new UsernamePasswordAuthenticationToken(
985
- principal, token, authorities
986
- );
987
- }
988
- }
989
- ```
990
-
991
- </details>
992
-
993
- ### レート制限とスロットリング
994
-
995
- ```plantuml
996
- @startuml
997
- title レート制限アルゴリズム
998
-
999
- rectangle "Token Bucket" as token_bucket {
1000
- note right
1001
- ・バケットにトークンを蓄積
1002
- ・リクエスト時にトークン消費
1003
- ・バースト対応可能
1004
-
1005
- 設定例:
1006
- rate: 100/分
1007
- burst: 20
1008
- end note
1009
- }
1010
-
1011
- rectangle "Sliding Window" as sliding_window {
1012
- note right
1013
- ・時間枠内のリクエスト数をカウント
1014
- ・枠をスライドして計算
1015
- ・より正確な制限
1016
-
1017
- 設定例:
1018
- window: 1分
1019
- limit: 100
1020
- end note
1021
- }
1022
-
1023
- rectangle "Fixed Window" as fixed_window {
1024
- note right
1025
- ・固定時間枠でカウント
1026
- ・シンプルな実装
1027
- ・境界でバーストの可能性
1028
-
1029
- 設定例:
1030
- window: 1分
1031
- limit: 100
1032
- end note
1033
- }
1034
-
1035
- @enduml
1036
- ```
1037
-
1038
- <details>
1039
- <summary>Spring Cloud Gateway 設定例</summary>
1040
-
1041
- ```yaml
1042
- # application.yml
1043
- spring:
1044
- cloud:
1045
- gateway:
1046
- routes:
1047
- - id: sales-service
1048
- uri: lb://sales-service
1049
- predicates:
1050
- - Path=/api/v1/sales/**
1051
- filters:
1052
- - name: RequestRateLimiter
1053
- args:
1054
- redis-rate-limiter.replenishRate: 100
1055
- redis-rate-limiter.burstCapacity: 200
1056
- key-resolver: "#{@userKeyResolver}"
1057
- - name: CircuitBreaker
1058
- args:
1059
- name: salesCircuitBreaker
1060
- fallbackUri: forward:/fallback/sales
1061
-
1062
- - id: accounting-service
1063
- uri: lb://accounting-service
1064
- predicates:
1065
- - Path=/api/v1/accounting/**
1066
- filters:
1067
- - name: RequestRateLimiter
1068
- args:
1069
- redis-rate-limiter.replenishRate: 50
1070
- redis-rate-limiter.burstCapacity: 100
1071
-
1072
- default-filters:
1073
- - name: Retry
1074
- args:
1075
- retries: 3
1076
- statuses: BAD_GATEWAY,SERVICE_UNAVAILABLE
1077
- methods: GET
1078
- backoff:
1079
- firstBackoff: 100ms
1080
- maxBackoff: 500ms
1081
- factor: 2
1082
- ```
1083
-
1084
- ```java
1085
- // Key Resolver(ユーザー単位のレート制限)
1086
- @Configuration
1087
- public class RateLimiterConfig {
1088
-
1089
- @Bean
1090
- public KeyResolver userKeyResolver() {
1091
- return exchange -> {
1092
- String userId = exchange.getRequest()
1093
- .getHeaders()
1094
- .getFirst("X-User-Id");
1095
- return Mono.just(userId != null ? userId : "anonymous");
1096
- };
1097
- }
1098
-
1099
- @Bean
1100
- public KeyResolver apiKeyResolver() {
1101
- return exchange -> {
1102
- String apiKey = exchange.getRequest()
1103
- .getHeaders()
1104
- .getFirst("X-API-Key");
1105
- return Mono.just(apiKey != null ? apiKey : "default");
1106
- };
1107
- }
1108
- }
1109
- ```
1110
-
1111
- </details>
1112
-
1113
- ### ログ集約とモニタリング
1114
-
1115
- ```plantuml
1116
- @startuml
1117
- title 分散トレーシングとログ集約
1118
-
1119
- rectangle "クライアント" as client
1120
-
1121
- rectangle "API Gateway" as gateway {
1122
- rectangle "トレースID生成" as trace_gen
1123
- }
1124
-
1125
- rectangle "サービス群" as services {
1126
- rectangle "販売サービス" as sales
1127
- rectangle "在庫サービス" as inventory
1128
- rectangle "会計サービス" as accounting
1129
- }
1130
-
1131
- rectangle "観測基盤" as observability {
1132
- database "ログ集約\n(ELK/Loki)" as logs
1133
- database "メトリクス\n(Prometheus)" as metrics
1134
- database "トレース\n(Jaeger/Zipkin)" as traces
1135
- }
1136
-
1137
- rectangle "ダッシュボード" as dashboard {
1138
- rectangle "Grafana" as grafana
1139
- rectangle "Kibana" as kibana
1140
- }
1141
-
1142
- client --> gateway : リクエスト
1143
- gateway --> sales : traceId
1144
- sales --> inventory : traceId
1145
- sales --> accounting : traceId
1146
-
1147
- gateway --> logs
1148
- sales --> logs
1149
- inventory --> logs
1150
- accounting --> logs
1151
-
1152
- gateway --> metrics
1153
- sales --> metrics
1154
- inventory --> metrics
1155
- accounting --> metrics
1156
-
1157
- gateway --> traces
1158
- sales --> traces
1159
- inventory --> traces
1160
- accounting --> traces
1161
-
1162
- logs --> kibana
1163
- metrics --> grafana
1164
- traces --> grafana
1165
-
1166
- @enduml
1167
- ```
1168
-
1169
- <details>
1170
- <summary>Java 実装例(Micrometer + OpenTelemetry)</summary>
1171
-
1172
- ```java
1173
- // トレーシング設定
1174
- @Configuration
1175
- public class TracingConfig {
1176
-
1177
- @Bean
1178
- public Tracer tracer() {
1179
- return GlobalOpenTelemetry.getTracer("sales-service");
1180
- }
1181
- }
1182
-
1183
- // カスタムメトリクス
1184
- @Component
1185
- public class OrderMetrics {
1186
- private final MeterRegistry meterRegistry;
1187
- private final Counter orderCreatedCounter;
1188
- private final Timer orderProcessingTimer;
1189
-
1190
- public OrderMetrics(MeterRegistry meterRegistry) {
1191
- this.meterRegistry = meterRegistry;
1192
-
1193
- this.orderCreatedCounter = Counter.builder("orders.created")
1194
- .description("Number of orders created")
1195
- .tag("service", "sales")
1196
- .register(meterRegistry);
1197
-
1198
- this.orderProcessingTimer = Timer.builder("orders.processing.time")
1199
- .description("Order processing time")
1200
- .tag("service", "sales")
1201
- .register(meterRegistry);
1202
- }
1203
-
1204
- public void recordOrderCreated(String status) {
1205
- orderCreatedCounter.increment();
1206
- meterRegistry.counter("orders.created.by.status",
1207
- "status", status).increment();
1208
- }
1209
-
1210
- public void recordProcessingTime(long durationMs) {
1211
- orderProcessingTimer.record(Duration.ofMillis(durationMs));
1212
- }
1213
- }
1214
-
1215
- // 構造化ログ
1216
- @Slf4j
1217
- @Service
1218
- public class OrderService {
1219
-
1220
- public Order createOrder(CreateOrderRequest request) {
1221
- MDC.put("orderId", request.getOrderId());
1222
- MDC.put("customerId", request.getCustomerId());
1223
-
1224
- try {
1225
- log.info("Creating order", kv("action", "create_order_start"));
1226
-
1227
- Order order = processOrder(request);
1228
-
1229
- log.info("Order created successfully",
1230
- kv("action", "create_order_complete"),
1231
- kv("totalAmount", order.getTotalAmount()));
1232
-
1233
- return order;
1234
- } catch (Exception e) {
1235
- log.error("Failed to create order",
1236
- kv("action", "create_order_failed"),
1237
- kv("error", e.getMessage()), e);
1238
- throw e;
1239
- } finally {
1240
- MDC.clear();
1241
- }
1242
- }
1243
- }
1244
- ```
1245
-
1246
- </details>
1247
-
1248
- ---
1249
-
1250
- ## 38.4 API インテグレーションテスト
1251
-
1252
- ### テストコンテナによる統合テスト環境
1253
-
1254
- ```plantuml
1255
- @startuml
1256
- title テストコンテナアーキテクチャ
1257
-
1258
- rectangle "テスト実行環境" as test_env {
1259
- rectangle "JUnit 5" as junit
1260
- rectangle "テストクラス" as test_class
1261
- }
1262
-
1263
- rectangle "Testcontainers" as testcontainers {
1264
- rectangle "PostgreSQL\nコンテナ" as postgres
1265
- rectangle "RabbitMQ\nコンテナ" as rabbitmq
1266
- rectangle "Redis\nコンテナ" as redis
1267
- }
1268
-
1269
- rectangle "アプリケーション" as app {
1270
- rectangle "Spring Boot\nアプリケーション" as spring
1271
- }
1272
-
1273
- test_class --> spring : HTTP リクエスト
1274
- spring --> postgres : データアクセス
1275
- spring --> rabbitmq : メッセージング
1276
- spring --> redis : キャッシュ
1277
-
1278
- junit --> testcontainers : コンテナ起動
1279
- testcontainers --> postgres
1280
- testcontainers --> rabbitmq
1281
- testcontainers --> redis
1282
-
1283
- note bottom of testcontainers
1284
- 【Testcontainersの利点】
1285
- ・本番同等の環境でテスト
1286
- ・テスト間の分離
1287
- ・CI/CDでの再現性
1288
- ・自動クリーンアップ
1289
- end note
1290
-
1291
- @enduml
1292
- ```
1293
-
1294
- <details>
1295
- <summary>Java 実装例</summary>
1296
-
1297
- ```java
1298
- // テストコンテナ設定
1299
- @Testcontainers
1300
- @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
1301
- @ActiveProfiles("test")
1302
- public abstract class IntegrationTestBase {
1303
-
1304
- @Container
1305
- static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
1306
- .withDatabaseName("testdb")
1307
- .withUsername("test")
1308
- .withPassword("test");
1309
-
1310
- @Container
1311
- static RabbitMQContainer rabbitmq = new RabbitMQContainer("rabbitmq:3.12-management");
1312
-
1313
- @DynamicPropertySource
1314
- static void configureProperties(DynamicPropertyRegistry registry) {
1315
- registry.add("spring.datasource.url", postgres::getJdbcUrl);
1316
- registry.add("spring.datasource.username", postgres::getUsername);
1317
- registry.add("spring.datasource.password", postgres::getPassword);
1318
- registry.add("spring.rabbitmq.host", rabbitmq::getHost);
1319
- registry.add("spring.rabbitmq.port", rabbitmq::getAmqpPort);
1320
- }
1321
-
1322
- @Autowired
1323
- protected TestRestTemplate restTemplate;
1324
-
1325
- @Autowired
1326
- protected JdbcTemplate jdbcTemplate;
1327
-
1328
- @BeforeEach
1329
- void setUp() {
1330
- // テストデータのクリーンアップ
1331
- cleanupTestData();
1332
- }
1333
-
1334
- protected void cleanupTestData() {
1335
- jdbcTemplate.execute("TRUNCATE TABLE 受注明細 CASCADE");
1336
- jdbcTemplate.execute("TRUNCATE TABLE 受注 CASCADE");
1337
- }
1338
- }
1339
- ```
1340
-
1341
- </details>
1342
-
1343
- ### REST API エンドポイントのテスト
1344
-
1345
- ```plantuml
1346
- @startuml
1347
- title API テストの構造
1348
-
1349
- ' ノード間の間隔を調整
1350
- skinparam nodesep 50
1351
- skinparam ranksep 50
1352
-
1353
- rectangle "== Given(前提条件)\n\n・テストデータ準備\n・認証トークン取得\n・モックの設定" as given
1354
-
1355
- rectangle "== When(操作)\n\n・APIエンドポイント呼び出し\n・リクエストボディ設定\n・ヘッダー設定" as when
1356
-
1357
- rectangle "== Then(検証)\n\n・ステータスコード確認\n・レスポンスボディ検証\n・データベース状態確認\n・イベント発行確認" as then
1358
-
1359
- given --> when
1360
- when --> then
1361
-
1362
- @enduml
1363
- ```
1364
-
1365
- <details>
1366
- <summary>Java 実装例</summary>
1367
-
1368
- ```java
1369
- // 受注API統合テスト
1370
- class OrderApiIntegrationTest extends IntegrationTestBase {
1371
-
1372
- @Autowired
1373
- private CustomerRepository customerRepository;
1374
-
1375
- @Autowired
1376
- private ProductRepository productRepository;
1377
-
1378
- private String authToken;
1379
- private Customer testCustomer;
1380
- private Product testProduct;
1381
-
1382
- @BeforeEach
1383
- void setUpTestData() {
1384
- // 認証トークン取得
1385
- authToken = getAuthToken("test-user", "password");
1386
-
1387
- // テストデータ準備
1388
- testCustomer = customerRepository.save(
1389
- new Customer("CUS-001", "テスト顧客", "test@example.com")
1390
- );
1391
- testProduct = productRepository.save(
1392
- new Product("PRD-001", "テスト商品", new BigDecimal("1000"))
1393
- );
1394
- }
1395
-
1396
- @Test
1397
- @DisplayName("受注登録: 正常系")
1398
- void createOrder_Success() {
1399
- // Given
1400
- CreateOrderRequest request = CreateOrderRequest.builder()
1401
- .customerId(testCustomer.getCustomerId())
1402
- .orderDate(LocalDate.now())
1403
- .lines(List.of(
1404
- OrderLineRequest.builder()
1405
- .productId(testProduct.getProductId())
1406
- .quantity(10)
1407
- .build()
1408
- ))
1409
- .build();
1410
-
1411
- // When
1412
- ResponseEntity<OrderResponse> response = restTemplate.exchange(
1413
- "/api/v1/sales/orders",
1414
- HttpMethod.POST,
1415
- new HttpEntity<>(request, createAuthHeaders()),
1416
- OrderResponse.class
1417
- );
1418
-
1419
- // Then
1420
- assertThat(response.getStatusCode()).isEqualTo(HttpStatus.CREATED);
1421
- assertThat(response.getBody()).isNotNull();
1422
- assertThat(response.getBody().getOrderId()).isNotBlank();
1423
- assertThat(response.getBody().getStatus()).isEqualTo("DRAFT");
1424
- assertThat(response.getBody().getTotalAmount())
1425
- .isEqualByComparingTo(new BigDecimal("10000"));
1426
-
1427
- // データベース状態確認
1428
- Order savedOrder = orderRepository.findById(
1429
- response.getBody().getOrderId()
1430
- ).orElseThrow();
1431
- assertThat(savedOrder.getCustomerId()).isEqualTo(testCustomer.getCustomerId());
1432
- assertThat(savedOrder.getLines()).hasSize(1);
1433
- }
1434
-
1435
- @Test
1436
- @DisplayName("受注登録: バリデーションエラー")
1437
- void createOrder_ValidationError() {
1438
- // Given - 明細なしのリクエスト
1439
- CreateOrderRequest request = CreateOrderRequest.builder()
1440
- .customerId(testCustomer.getCustomerId())
1441
- .orderDate(LocalDate.now())
1442
- .lines(List.of()) // 空の明細
1443
- .build();
1444
-
1445
- // When
1446
- ResponseEntity<ProblemDetail> response = restTemplate.exchange(
1447
- "/api/v1/sales/orders",
1448
- HttpMethod.POST,
1449
- new HttpEntity<>(request, createAuthHeaders()),
1450
- ProblemDetail.class
1451
- );
1452
-
1453
- // Then
1454
- assertThat(response.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
1455
- assertThat(response.getBody().getTitle()).isEqualTo("Validation Error");
1456
- }
1457
-
1458
- @Test
1459
- @DisplayName("受注一覧取得: ページング")
1460
- void getOrders_Paging() {
1461
- // Given - 複数の受注を作成
1462
- for (int i = 0; i < 25; i++) {
1463
- createTestOrder("ORD-" + String.format("%03d", i));
1464
- }
1465
-
1466
- // When
1467
- ResponseEntity<PagedResponse<OrderSummary>> response = restTemplate.exchange(
1468
- "/api/v1/sales/orders?page=0&size=10",
1469
- HttpMethod.GET,
1470
- new HttpEntity<>(createAuthHeaders()),
1471
- new ParameterizedTypeReference<>() {}
1472
- );
1473
-
1474
- // Then
1475
- assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
1476
- assertThat(response.getBody().getContent()).hasSize(10);
1477
- assertThat(response.getBody().getTotalElements()).isEqualTo(25);
1478
- assertThat(response.getBody().getTotalPages()).isEqualTo(3);
1479
- }
1480
-
1481
- private HttpHeaders createAuthHeaders() {
1482
- HttpHeaders headers = new HttpHeaders();
1483
- headers.setBearerAuth(authToken);
1484
- headers.setContentType(MediaType.APPLICATION_JSON);
1485
- return headers;
1486
- }
1487
- }
1488
- ```
1489
-
1490
- </details>
1491
-
1492
- ### サービス間連携テスト
1493
-
1494
- ```plantuml
1495
- @startuml
1496
- title サービス間連携テストのアプローチ
1497
-
1498
- ' ボックスの定義
1499
- rectangle "Contract Testing" as contract
1500
- note right of contract
1501
- ・Pact / Spring Cloud Contract
1502
- ・プロバイダーとコンシューマーの契約
1503
- ・独立したテスト実行
1504
- end note
1505
-
1506
- rectangle "Component Testing" as component
1507
- note right of component
1508
- ・WireMockで外部サービスをモック
1509
- ・単一サービスの統合テスト
1510
- ・高速なフィードバック
1511
- end note
1512
-
1513
- rectangle "End-to-End Testing" as e2e
1514
- note right of e2e
1515
- ・全サービスを起動
1516
- ・Docker Composeで環境構築
1517
- ・本番同等のシナリオテスト
1518
- end note
1519
-
1520
- ' 配置(上から下に並べる)
1521
- contract -[hidden]down-> component
1522
- component -[hidden]down-> e2e
1523
-
1524
- @enduml
1525
- ```
1526
-
1527
- <details>
1528
- <summary>Java 実装例(WireMock)</summary>
1529
-
1530
- ```java
1531
- // WireMockを使用したサービス間連携テスト
1532
- @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
1533
- @AutoConfigureWireMock(port = 0)
1534
- class OrderServiceWithInventoryTest {
1535
-
1536
- @Autowired
1537
- private TestRestTemplate restTemplate;
1538
-
1539
- @Value("${wiremock.server.port}")
1540
- private int wireMockPort;
1541
-
1542
- @BeforeEach
1543
- void setUp() {
1544
- // 在庫サービスのモック設定
1545
- stubFor(get(urlPathMatching("/api/v1/inventory/.*"))
1546
- .willReturn(aResponse()
1547
- .withStatus(200)
1548
- .withHeader("Content-Type", "application/json")
1549
- .withBody("""
1550
- {
1551
- "productId": "PRD-001",
1552
- "quantity": 100,
1553
- "allocatedQuantity": 0,
1554
- "availableQuantity": 100
1555
- }
1556
- """)));
1557
-
1558
- stubFor(post(urlPathMatching("/api/v1/inventory/allocate"))
1559
- .willReturn(aResponse()
1560
- .withStatus(200)
1561
- .withHeader("Content-Type", "application/json")
1562
- .withBody("""
1563
- {
1564
- "success": true,
1565
- "allocationId": "ALLOC-001"
1566
- }
1567
- """)));
1568
- }
1569
-
1570
- @Test
1571
- @DisplayName("受注確定: 在庫引当成功")
1572
- void confirmOrder_InventoryAllocated() {
1573
- // Given
1574
- String orderId = createDraftOrder();
1575
-
1576
- // When
1577
- ResponseEntity<OrderResponse> response = restTemplate.exchange(
1578
- "/api/v1/sales/orders/{orderId}/confirm",
1579
- HttpMethod.POST,
1580
- new HttpEntity<>(createAuthHeaders()),
1581
- OrderResponse.class,
1582
- orderId
1583
- );
1584
-
1585
- // Then
1586
- assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
1587
- assertThat(response.getBody().getStatus()).isEqualTo("CONFIRMED");
1588
-
1589
- // 在庫サービスへの呼び出しを検証
1590
- verify(postRequestedFor(urlPathEqualTo("/api/v1/inventory/allocate"))
1591
- .withRequestBody(matchingJsonPath("$.productId", equalTo("PRD-001")))
1592
- .withRequestBody(matchingJsonPath("$.quantity", equalTo("10"))));
1593
- }
1594
-
1595
- @Test
1596
- @DisplayName("受注確定: 在庫不足")
1597
- void confirmOrder_InsufficientStock() {
1598
- // Given - 在庫不足のレスポンスを設定
1599
- stubFor(post(urlPathMatching("/api/v1/inventory/allocate"))
1600
- .willReturn(aResponse()
1601
- .withStatus(200)
1602
- .withBody("""
1603
- {
1604
- "success": false,
1605
- "errorMessage": "Insufficient stock"
1606
- }
1607
- """)));
1608
-
1609
- String orderId = createDraftOrder();
1610
-
1611
- // When
1612
- ResponseEntity<ProblemDetail> response = restTemplate.exchange(
1613
- "/api/v1/sales/orders/{orderId}/confirm",
1614
- HttpMethod.POST,
1615
- new HttpEntity<>(createAuthHeaders()),
1616
- ProblemDetail.class,
1617
- orderId
1618
- );
1619
-
1620
- // Then
1621
- assertThat(response.getStatusCode()).isEqualTo(HttpStatus.CONFLICT);
1622
- assertThat(response.getBody().getDetail()).contains("在庫不足");
1623
- }
1624
-
1625
- @Test
1626
- @DisplayName("受注確定: 在庫サービスタイムアウト")
1627
- void confirmOrder_InventoryServiceTimeout() {
1628
- // Given - タイムアウトをシミュレート
1629
- stubFor(post(urlPathMatching("/api/v1/inventory/allocate"))
1630
- .willReturn(aResponse()
1631
- .withFixedDelay(5000) // 5秒遅延
1632
- .withStatus(200)));
1633
-
1634
- String orderId = createDraftOrder();
1635
-
1636
- // When
1637
- ResponseEntity<ProblemDetail> response = restTemplate.exchange(
1638
- "/api/v1/sales/orders/{orderId}/confirm",
1639
- HttpMethod.POST,
1640
- new HttpEntity<>(createAuthHeaders()),
1641
- ProblemDetail.class,
1642
- orderId
1643
- );
1644
-
1645
- // Then - サーキットブレーカーによるフォールバック
1646
- assertThat(response.getStatusCode()).isEqualTo(HttpStatus.SERVICE_UNAVAILABLE);
1647
- }
1648
- }
1649
- ```
1650
-
1651
- </details>
1652
-
1653
- ---
1654
-
1655
- ## 38.5 まとめ
1656
-
1657
- 本章では、API 設計とサービス連携のパターンについて解説しました。
1658
-
1659
- ### 学んだこと
1660
-
1661
- 1. **API 設計の原則**
1662
-
1663
- - RESTful API の制約と設計原則
1664
- - リソース指向設計(名詞ベース、HTTP メソッド)
1665
- - バージョニング戦略(URI パス推奨)
1666
-
1667
- 2. **サービス間通信**
1668
-
1669
- - 同期通信(REST、gRPC)の使い分け
1670
- - 非同期通信(メッセージキュー)
1671
- - サーキットブレーカーによる障害対策
1672
-
1673
- 3. **API ゲートウェイ**
1674
-
1675
- - 認証・認可の一元化(JWT)
1676
- - レート制限とスロットリング
1677
- - ログ集約とモニタリング
1678
-
1679
- 4. **API インテグレーションテスト**
1680
-
1681
- - テストコンテナによる統合テスト環境
1682
- - REST API エンドポイントのテスト
1683
- - サービス間連携テスト(WireMock)
1684
-
1685
- ### API 設計チェックリスト
1686
-
1687
- - [ ] リソースは名詞(複数形)で命名されているか
1688
- - [ ] HTTP メソッドが正しく使用されているか
1689
- - [ ] エラーレスポンスは Problem Detail 形式か
1690
- - [ ] バージョニング戦略が決定されているか
1691
- - [ ] 認証・認可が適切に実装されているか
1692
- - [ ] レート制限が設定されているか
1693
- - [ ] サーキットブレーカーが導入されているか
1694
- - [ ] 統合テストが整備されているか
1695
-
1696
- ### 次章の予告
1697
-
1698
- 第39章では、データ連携の実装パターンについて解説します。バッチ連携、リアルタイム連携、連携テーブルの設計など、具体的な実装パターンを学びます。
1
+ # 第38章:API 設計とサービス連携
2
+
3
+ 本章では、基幹業務システムにおける API 設計の原則と、サービス間連携のパターンについて解説します。RESTful API の設計、サービス間通信の方式、API ゲートウェイの活用、そしてインテグレーションテストの実践方法を学びます。
4
+
5
+ ---
6
+
7
+ ## 38.1 API 設計の原則
8
+
9
+ ### RESTful API の設計
10
+
11
+ REST(Representational State Transfer)は、Web API 設計の標準的なアーキテクチャスタイルです。
12
+
13
+ ```plantuml
14
+ @startuml
15
+ title RESTful API の基本原則
16
+
17
+ rectangle "REST の制約" as constraints {
18
+ rectangle "クライアント-サーバー" as cs {
19
+ note right
20
+ 関心の分離
21
+ 独立した進化
22
+ end note
23
+ }
24
+
25
+ rectangle "ステートレス" as stateless {
26
+ note right
27
+ 各リクエストは独立
28
+ セッション状態を保持しない
29
+ end note
30
+ }
31
+
32
+ rectangle "キャッシュ可能" as cacheable {
33
+ note right
34
+ レスポンスにキャッシュ可否を明示
35
+ パフォーマンス向上
36
+ end note
37
+ }
38
+
39
+ rectangle "統一インターフェース" as uniform {
40
+ note right
41
+ リソース識別(URI)
42
+ 表現によるリソース操作
43
+ 自己記述メッセージ
44
+ HATEOAS
45
+ end note
46
+ }
47
+
48
+ rectangle "階層化システム" as layered {
49
+ note right
50
+ 中間サーバーの追加が可能
51
+ ロードバランサー、キャッシュ等
52
+ end note
53
+ }
54
+ }
55
+
56
+ @enduml
57
+ ```
58
+
59
+ #### HTTP メソッドとリソース操作
60
+
61
+ | HTTP メソッド | 操作 | 冪等性 | 安全性 | 使用例 |
62
+ |-------------|-----|-------|-------|-------|
63
+ | GET | 取得 | Yes | Yes | リソースの参照 |
64
+ | POST | 作成 | No | No | 新規リソース作成 |
65
+ | PUT | 置換 | Yes | No | リソース全体の更新 |
66
+ | PATCH | 部分更新 | No | No | リソースの一部更新 |
67
+ | DELETE | 削除 | Yes | No | リソースの削除 |
68
+
69
+ ### リソース指向設計
70
+
71
+ API はリソース(名詞)を中心に設計し、操作は HTTP メソッドで表現します。
72
+
73
+ ```plantuml
74
+ @startuml
75
+ title リソース指向 API 設計
76
+
77
+ package "販売管理 API" as sales_api {
78
+ rectangle "/orders" as orders {
79
+ rectangle "GET /orders" as get_orders
80
+ rectangle "POST /orders" as post_order
81
+ rectangle "GET /orders/{id}" as get_order
82
+ rectangle "PUT /orders/{id}" as put_order
83
+ rectangle "DELETE /orders/{id}" as delete_order
84
+ }
85
+
86
+ rectangle "/orders/{id}/lines" as order_lines {
87
+ rectangle "GET /orders/{id}/lines" as get_lines
88
+ rectangle "POST /orders/{id}/lines" as post_line
89
+ }
90
+
91
+ rectangle "/customers" as customers {
92
+ rectangle "GET /customers" as get_customers
93
+ rectangle "POST /customers" as post_customer
94
+ rectangle "GET /customers/{id}" as get_customer
95
+ }
96
+
97
+ rectangle "/customers/{id}/orders" as customer_orders {
98
+ rectangle "GET /customers/{id}/orders" as get_customer_orders
99
+ }
100
+ }
101
+
102
+ note bottom of sales_api
103
+ 【設計原則】
104
+ ・リソースは名詞(複数形)
105
+ ・操作はHTTPメソッドで表現
106
+ ・階層構造で関連を表現
107
+ ・クエリパラメータでフィルタリング
108
+ end note
109
+
110
+ @enduml
111
+ ```
112
+
113
+ #### 基幹業務システムの API エンドポイント設計
114
+
115
+ ```plantuml
116
+ @startuml
117
+ title 基幹業務システム API エンドポイント
118
+
119
+ package "販売管理 API" as sales {
120
+ rectangle "/api/v1/sales" as sales_base {
121
+ rectangle "受注: /orders"
122
+ rectangle "出荷: /shipments"
123
+ rectangle "売上: /sales"
124
+ rectangle "請求: /invoices"
125
+ rectangle "入金: /payments"
126
+ }
127
+ rectangle "/api/v1/sales/masters" as sales_masters {
128
+ rectangle "顧客: /customers"
129
+ rectangle "商品: /products"
130
+ }
131
+ }
132
+
133
+ package "財務会計 API" as accounting {
134
+ rectangle "/api/v1/accounting" as acc_base {
135
+ rectangle "仕訳: /journals"
136
+ rectangle "残高: /balances"
137
+ rectangle "試算表: /trial-balances"
138
+ }
139
+ rectangle "/api/v1/accounting/masters" as acc_masters {
140
+ rectangle "勘定科目: /accounts"
141
+ rectangle "補助科目: /sub-accounts"
142
+ }
143
+ }
144
+
145
+ package "生産管理 API" as production {
146
+ rectangle "/api/v1/production" as prod_base {
147
+ rectangle "製造指示: /work-orders"
148
+ rectangle "発注: /purchase-orders"
149
+ rectangle "在庫: /inventory"
150
+ }
151
+ rectangle "/api/v1/production/masters" as prod_masters {
152
+ rectangle "品目: /items"
153
+ rectangle "BOM: /bom"
154
+ rectangle "工程: /processes"
155
+ }
156
+ }
157
+
158
+ @enduml
159
+ ```
160
+
161
+ <details>
162
+ <summary>OpenAPI 定義例</summary>
163
+
164
+ ```yaml
165
+ openapi: 3.0.3
166
+ info:
167
+ title: 販売管理 API
168
+ version: 1.0.0
169
+ description: 基幹業務システム - 販売管理 API
170
+
171
+ servers:
172
+ - url: https://api.example.com/api/v1/sales
173
+ description: Production server
174
+
175
+ paths:
176
+ /orders:
177
+ get:
178
+ summary: 受注一覧取得
179
+ operationId: getOrders
180
+ parameters:
181
+ - name: customerId
182
+ in: query
183
+ schema:
184
+ type: string
185
+ - name: status
186
+ in: query
187
+ schema:
188
+ type: string
189
+ enum: [DRAFT, CONFIRMED, SHIPPED, COMPLETED]
190
+ - name: fromDate
191
+ in: query
192
+ schema:
193
+ type: string
194
+ format: date
195
+ - name: toDate
196
+ in: query
197
+ schema:
198
+ type: string
199
+ format: date
200
+ - name: page
201
+ in: query
202
+ schema:
203
+ type: integer
204
+ default: 0
205
+ - name: size
206
+ in: query
207
+ schema:
208
+ type: integer
209
+ default: 20
210
+ responses:
211
+ '200':
212
+ description: 成功
213
+ content:
214
+ application/json:
215
+ schema:
216
+ $ref: '#/components/schemas/OrderListResponse'
217
+
218
+ post:
219
+ summary: 受注登録
220
+ operationId: createOrder
221
+ requestBody:
222
+ required: true
223
+ content:
224
+ application/json:
225
+ schema:
226
+ $ref: '#/components/schemas/CreateOrderRequest'
227
+ responses:
228
+ '201':
229
+ description: 作成成功
230
+ content:
231
+ application/json:
232
+ schema:
233
+ $ref: '#/components/schemas/OrderResponse'
234
+ '400':
235
+ description: バリデーションエラー
236
+ content:
237
+ application/problem+json:
238
+ schema:
239
+ $ref: '#/components/schemas/ProblemDetail'
240
+
241
+ /orders/{orderId}:
242
+ get:
243
+ summary: 受注詳細取得
244
+ operationId: getOrder
245
+ parameters:
246
+ - name: orderId
247
+ in: path
248
+ required: true
249
+ schema:
250
+ type: string
251
+ responses:
252
+ '200':
253
+ description: 成功
254
+ content:
255
+ application/json:
256
+ schema:
257
+ $ref: '#/components/schemas/OrderResponse'
258
+ '404':
259
+ description: 受注が見つからない
260
+
261
+ components:
262
+ schemas:
263
+ OrderResponse:
264
+ type: object
265
+ properties:
266
+ orderId:
267
+ type: string
268
+ customerId:
269
+ type: string
270
+ customerName:
271
+ type: string
272
+ orderDate:
273
+ type: string
274
+ format: date
275
+ status:
276
+ type: string
277
+ enum: [DRAFT, CONFIRMED, SHIPPED, COMPLETED]
278
+ totalAmount:
279
+ type: number
280
+ taxAmount:
281
+ type: number
282
+ lines:
283
+ type: array
284
+ items:
285
+ $ref: '#/components/schemas/OrderLineResponse'
286
+ _links:
287
+ $ref: '#/components/schemas/Links'
288
+
289
+ CreateOrderRequest:
290
+ type: object
291
+ required:
292
+ - customerId
293
+ - orderDate
294
+ - lines
295
+ properties:
296
+ customerId:
297
+ type: string
298
+ orderDate:
299
+ type: string
300
+ format: date
301
+ requestedDeliveryDate:
302
+ type: string
303
+ format: date
304
+ lines:
305
+ type: array
306
+ items:
307
+ $ref: '#/components/schemas/CreateOrderLineRequest'
308
+ minItems: 1
309
+
310
+ ProblemDetail:
311
+ type: object
312
+ properties:
313
+ type:
314
+ type: string
315
+ title:
316
+ type: string
317
+ status:
318
+ type: integer
319
+ detail:
320
+ type: string
321
+ instance:
322
+ type: string
323
+ ```
324
+
325
+ </details>
326
+
327
+ ### バージョニング戦略
328
+
329
+ API の互換性を維持しながら進化させるためのバージョニング戦略を検討します。
330
+
331
+ ```plantuml
332
+ @startuml
333
+ title API バージョニング戦略の比較
334
+
335
+ rectangle "URI パスバージョニング" as uri {
336
+ note right
337
+ /api/v1/orders
338
+ /api/v2/orders
339
+
340
+ 【メリット】
341
+ ・明確で分かりやすい
342
+ ・キャッシュしやすい
343
+
344
+ 【デメリット】
345
+ ・URIの変更が必要
346
+ end note
347
+ }
348
+
349
+ rectangle "クエリパラメータ" as query {
350
+ note right
351
+ /api/orders?version=1
352
+ /api/orders?version=2
353
+
354
+ 【メリット】
355
+ ・URIが変わらない
356
+
357
+ 【デメリット】
358
+ ・見落としやすい
359
+ end note
360
+ }
361
+
362
+ rectangle "カスタムヘッダー" as header {
363
+ note right
364
+ X-API-Version: 1
365
+ X-API-Version: 2
366
+
367
+ 【メリット】
368
+ ・URIがクリーン
369
+
370
+ 【デメリット】
371
+ ・テストしにくい
372
+ end note
373
+ }
374
+
375
+ rectangle "Accept ヘッダー" as accept {
376
+ note right
377
+ Accept: application/vnd.company.v1+json
378
+ Accept: application/vnd.company.v2+json
379
+
380
+ 【メリット】
381
+ ・HTTP標準に準拠
382
+
383
+ 【デメリット】
384
+ ・複雑
385
+ end note
386
+ }
387
+
388
+ @enduml
389
+ ```
390
+
391
+ #### 推奨:URI パスバージョニング
392
+
393
+ ```plantuml
394
+ @startuml
395
+ title URI パスバージョニングの運用
396
+
397
+ rectangle "v1(現行)" as v1 {
398
+ rectangle "/api/v1/orders" as v1_orders
399
+ rectangle "/api/v1/customers" as v1_customers
400
+ }
401
+
402
+ rectangle "v2(新版)" as v2 {
403
+ rectangle "/api/v2/orders" as v2_orders
404
+ rectangle "/api/v2/customers" as v2_customers
405
+ }
406
+
407
+ note bottom of v1
408
+ 【v1 → v2 移行】
409
+ 1. v2を新規リリース
410
+ 2. 並行運用期間
411
+ 3. v1を非推奨化(Deprecation)
412
+ 4. v1を廃止(Sunset)
413
+
414
+ 【Deprecation ヘッダー】
415
+ Deprecation: true
416
+ Sunset: Sat, 01 Jan 2025 00:00:00 GMT
417
+ end note
418
+
419
+ @enduml
420
+ ```
421
+
422
+ ---
423
+
424
+ ## 38.2 サービス間通信
425
+
426
+ ### 同期通信(REST / gRPC)
427
+
428
+ ```plantuml
429
+ @startuml
430
+ title 同期通信パターンの比較
431
+
432
+ rectangle "REST over HTTP" as rest {
433
+ rectangle "販売サービス" as sales_rest
434
+ rectangle "在庫サービス" as inv_rest
435
+
436
+ sales_rest --> inv_rest : HTTP/JSON
437
+ }
438
+
439
+ rectangle "gRPC" as grpc {
440
+ rectangle "販売サービス" as sales_grpc
441
+ rectangle "在庫サービス" as inv_grpc
442
+
443
+ sales_grpc --> inv_grpc : HTTP/2 + Protocol Buffers
444
+ }
445
+
446
+ note bottom of rest
447
+ 【REST】
448
+ ・テキストベース(JSON)
449
+ ・ブラウザから直接呼び出し可能
450
+ ・広く普及、ツール豊富
451
+
452
+ 【適用場面】
453
+ ・外部公開API
454
+ ・フロントエンド連携
455
+ end note
456
+
457
+ note bottom of grpc
458
+ 【gRPC】
459
+ ・バイナリプロトコル
460
+ ・高速、低レイテンシ
461
+ ・スキーマ定義必須
462
+
463
+ 【適用場面】
464
+ ・マイクロサービス間
465
+ ・高性能要件
466
+ end note
467
+
468
+ @enduml
469
+ ```
470
+
471
+ <details>
472
+ <summary>gRPC 定義例</summary>
473
+
474
+ ```protobuf
475
+ syntax = "proto3";
476
+
477
+ package sales.v1;
478
+
479
+ option java_package = "com.example.sales.grpc";
480
+ option java_multiple_files = true;
481
+
482
+ // 在庫サービス
483
+ service InventoryService {
484
+ // 在庫照会
485
+ rpc GetStock(GetStockRequest) returns (StockResponse);
486
+
487
+ // 在庫引当
488
+ rpc AllocateStock(AllocateStockRequest) returns (AllocateStockResponse);
489
+
490
+ // 引当解除
491
+ rpc ReleaseStock(ReleaseStockRequest) returns (ReleaseStockResponse);
492
+ }
493
+
494
+ message GetStockRequest {
495
+ string product_id = 1;
496
+ string warehouse_id = 2;
497
+ }
498
+
499
+ message StockResponse {
500
+ string product_id = 1;
501
+ string warehouse_id = 2;
502
+ int32 quantity = 3;
503
+ int32 allocated_quantity = 4;
504
+ int32 available_quantity = 5;
505
+ }
506
+
507
+ message AllocateStockRequest {
508
+ string product_id = 1;
509
+ string warehouse_id = 2;
510
+ int32 quantity = 3;
511
+ string order_id = 4;
512
+ }
513
+
514
+ message AllocateStockResponse {
515
+ bool success = 1;
516
+ string allocation_id = 2;
517
+ string error_message = 3;
518
+ }
519
+ ```
520
+
521
+ </details>
522
+
523
+ ### 非同期通信(メッセージキュー)
524
+
525
+ ```plantuml
526
+ @startuml
527
+ title 非同期通信パターン
528
+
529
+ rectangle "販売サービス" as sales
530
+
531
+ rectangle "メッセージブローカー" as broker {
532
+ queue "受注キュー" as order_queue
533
+ collections "売上トピック" as sales_topic
534
+ }
535
+
536
+ rectangle "在庫サービス" as inventory
537
+ rectangle "会計サービス" as accounting
538
+ rectangle "通知サービス" as notification
539
+
540
+ sales --> order_queue : 受注メッセージ
541
+ order_queue --> inventory : 在庫引当
542
+
543
+ sales --> sales_topic : 売上イベント
544
+ sales_topic --> accounting : 仕訳生成
545
+ sales_topic --> notification : 通知送信
546
+ sales_topic --> inventory : 在庫更新
547
+
548
+ note bottom of broker
549
+ 【Point-to-Point】
550
+ ・1対1の通信
551
+ ・キューで実現
552
+ ・負荷分散可能
553
+
554
+ 【Publish-Subscribe】
555
+ ・1対多の通信
556
+ ・トピックで実現
557
+ ・イベント配信
558
+ end note
559
+
560
+ @enduml
561
+ ```
562
+
563
+ <details>
564
+ <summary>Java 実装例(Spring AMQP)</summary>
565
+
566
+ ```java
567
+ // メッセージ送信
568
+ @Service
569
+ public class OrderMessagePublisher {
570
+ private final RabbitTemplate rabbitTemplate;
571
+
572
+ public void publishOrderCreated(OrderCreatedEvent event) {
573
+ rabbitTemplate.convertAndSend(
574
+ "sales.exchange",
575
+ "order.created",
576
+ event
577
+ );
578
+ }
579
+
580
+ public void publishSalesCompleted(SalesCompletedEvent event) {
581
+ rabbitTemplate.convertAndSend(
582
+ "sales.topic.exchange",
583
+ "sales.completed",
584
+ event
585
+ );
586
+ }
587
+ }
588
+
589
+ // メッセージ受信
590
+ @Component
591
+ public class OrderMessageListener {
592
+
593
+ @RabbitListener(queues = "inventory.order.queue")
594
+ public void handleOrderCreated(OrderCreatedEvent event) {
595
+ // 在庫引当処理
596
+ log.info("Received order: {}", event.orderId());
597
+ inventoryService.allocate(event);
598
+ }
599
+ }
600
+
601
+ // 設定
602
+ @Configuration
603
+ public class RabbitMQConfig {
604
+
605
+ @Bean
606
+ public TopicExchange salesTopicExchange() {
607
+ return new TopicExchange("sales.topic.exchange");
608
+ }
609
+
610
+ @Bean
611
+ public Queue accountingQueue() {
612
+ return new Queue("accounting.sales.queue", true);
613
+ }
614
+
615
+ @Bean
616
+ public Binding accountingBinding(Queue accountingQueue,
617
+ TopicExchange salesTopicExchange) {
618
+ return BindingBuilder.bind(accountingQueue)
619
+ .to(salesTopicExchange)
620
+ .with("sales.*");
621
+ }
622
+ }
623
+ ```
624
+
625
+ </details>
626
+
627
+ ### サーキットブレーカーパターン
628
+
629
+ サービス間通信の障害を検知し、障害の連鎖を防ぐパターンです。
630
+
631
+ ```plantuml
632
+ @startuml
633
+ title サーキットブレーカーの状態遷移
634
+
635
+ [*] --> Closed : 初期状態
636
+
637
+ Closed --> Open : 失敗が閾値を超過
638
+ Closed --> Closed : 成功
639
+
640
+ Open --> HalfOpen : タイムアウト後
641
+ Open --> Open : リクエスト拒否
642
+
643
+ HalfOpen --> Closed : 試行成功
644
+ HalfOpen --> Open : 試行失敗
645
+
646
+ note right of Closed
647
+ 【Closed】
648
+ 通常稼働
649
+ リクエストを通す
650
+ end note
651
+
652
+ note right of Open
653
+ 【Open】
654
+ 障害検知
655
+ 即座にエラー返却
656
+ (Fail Fast)
657
+ end note
658
+
659
+ note right of HalfOpen
660
+ 【Half-Open】
661
+ 復旧確認中
662
+ 限定的に試行
663
+ end note
664
+
665
+ @enduml
666
+ ```
667
+
668
+ ```plantuml
669
+ @startuml
670
+ title サーキットブレーカーの動作フロー
671
+
672
+ |クライアント|
673
+ start
674
+ :サービス呼び出し;
675
+
676
+ |サーキットブレーカー|
677
+ if (回路状態?) then (Open)
678
+ :即座にフォールバック;
679
+ |クライアント|
680
+ :フォールバック結果;
681
+ stop
682
+ else (Closed/HalfOpen)
683
+ endif
684
+
685
+ :下流サービス呼び出し;
686
+
687
+ |下流サービス|
688
+ if (処理成功?) then (yes)
689
+ :正常レスポンス;
690
+ |サーキットブレーカー|
691
+ :成功をカウント;
692
+ :Closedを維持/遷移;
693
+ else (no)
694
+ :エラー/タイムアウト;
695
+ |サーキットブレーカー|
696
+ :失敗をカウント;
697
+
698
+ if (失敗数 >= 閾値?) then (yes)
699
+ :Openに遷移;
700
+ endif
701
+ endif
702
+
703
+ |クライアント|
704
+ :結果を返却;
705
+
706
+ stop
707
+
708
+ @enduml
709
+ ```
710
+
711
+ <details>
712
+ <summary>Java 実装例(Resilience4j)</summary>
713
+
714
+ ```java
715
+ // 設定
716
+ @Configuration
717
+ public class CircuitBreakerConfig {
718
+
719
+ @Bean
720
+ public CircuitBreakerRegistry circuitBreakerRegistry() {
721
+ CircuitBreakerConfig config = CircuitBreakerConfig.custom()
722
+ .failureRateThreshold(50) // 失敗率50%でOpen
723
+ .waitDurationInOpenState(Duration.ofSeconds(30))
724
+ .permittedNumberOfCallsInHalfOpenState(3)
725
+ .slidingWindowType(SlidingWindowType.COUNT_BASED)
726
+ .slidingWindowSize(10)
727
+ .build();
728
+
729
+ return CircuitBreakerRegistry.of(config);
730
+ }
731
+ }
732
+
733
+ // サービス
734
+ @Service
735
+ public class InventoryServiceClient {
736
+ private final RestTemplate restTemplate;
737
+ private final CircuitBreaker circuitBreaker;
738
+
739
+ public InventoryServiceClient(RestTemplate restTemplate,
740
+ CircuitBreakerRegistry registry) {
741
+ this.restTemplate = restTemplate;
742
+ this.circuitBreaker = registry.circuitBreaker("inventoryService");
743
+ }
744
+
745
+ public StockResponse getStock(String productId) {
746
+ Supplier<StockResponse> supplier = CircuitBreaker
747
+ .decorateSupplier(circuitBreaker, () -> {
748
+ return restTemplate.getForObject(
749
+ "/api/v1/inventory/{productId}",
750
+ StockResponse.class,
751
+ productId
752
+ );
753
+ });
754
+
755
+ return Try.ofSupplier(supplier)
756
+ .recover(CallNotPermittedException.class,
757
+ e -> getFallbackStock(productId))
758
+ .recover(Exception.class,
759
+ e -> getFallbackStock(productId))
760
+ .get();
761
+ }
762
+
763
+ private StockResponse getFallbackStock(String productId) {
764
+ // フォールバック: キャッシュから取得または推定値
765
+ return new StockResponse(productId, 0, 0, 0, "UNKNOWN");
766
+ }
767
+ }
768
+
769
+ // アノテーションベース
770
+ @Service
771
+ public class AccountingServiceClient {
772
+
773
+ @CircuitBreaker(name = "accountingService", fallbackMethod = "fallback")
774
+ @Retry(name = "accountingService")
775
+ @TimeLimiter(name = "accountingService")
776
+ public CompletableFuture<JournalResponse> createJournal(
777
+ CreateJournalRequest request) {
778
+ return CompletableFuture.supplyAsync(() -> {
779
+ return restTemplate.postForObject(
780
+ "/api/v1/accounting/journals",
781
+ request,
782
+ JournalResponse.class
783
+ );
784
+ });
785
+ }
786
+
787
+ public CompletableFuture<JournalResponse> fallback(
788
+ CreateJournalRequest request, Throwable t) {
789
+ log.warn("Fallback for createJournal: {}", t.getMessage());
790
+ // 仕訳をキューに保存して後で再試行
791
+ pendingJournalQueue.add(request);
792
+ return CompletableFuture.completedFuture(
793
+ JournalResponse.pending(request.getCorrelationId())
794
+ );
795
+ }
796
+ }
797
+ ```
798
+
799
+ </details>
800
+
801
+ ---
802
+
803
+ ## 38.3 API ゲートウェイ
804
+
805
+ API ゲートウェイは、クライアントと複数のバックエンドサービスの間に位置し、横断的な関心事を一元的に処理します。
806
+
807
+ ```plantuml
808
+ @startuml
809
+ title API ゲートウェイアーキテクチャ
810
+
811
+ rectangle "クライアント" as clients {
812
+ rectangle "Webアプリ" as web
813
+ rectangle "モバイルアプリ" as mobile
814
+ rectangle "外部システム" as external
815
+ }
816
+
817
+ rectangle "API Gateway" as gateway {
818
+ rectangle "認証・認可" as auth
819
+ rectangle "レート制限" as rate
820
+ rectangle "ルーティング" as routing
821
+ rectangle "ログ・監視" as logging
822
+ rectangle "変換・集約" as transform
823
+ }
824
+
825
+ rectangle "バックエンドサービス" as backend {
826
+ rectangle "販売API" as sales
827
+ rectangle "会計API" as accounting
828
+ rectangle "生産API" as production
829
+ }
830
+
831
+ web --> gateway
832
+ mobile --> gateway
833
+ external --> gateway
834
+
835
+ gateway --> sales
836
+ gateway --> accounting
837
+ gateway --> production
838
+
839
+ note bottom of gateway
840
+ 【API Gatewayの責務】
841
+ ・認証・認可の一元化
842
+ ・レート制限
843
+ ・リクエストルーティング
844
+ ・ログ集約
845
+ ・レスポンス変換
846
+ ・API集約
847
+ end note
848
+
849
+ @enduml
850
+ ```
851
+
852
+ ### 認証・認可の一元化
853
+
854
+ ```plantuml
855
+ @startuml
856
+ title JWT 認証フロー
857
+
858
+ |クライアント|
859
+ start
860
+ :認証リクエスト;
861
+ note right
862
+ POST /auth/login
863
+ {username, password}
864
+ end note
865
+
866
+ |認証サービス|
867
+ :認証情報検証;
868
+ :JWTトークン発行;
869
+ note right
870
+ Header: {alg, typ}
871
+ Payload: {sub, roles, exp}
872
+ Signature
873
+ end note
874
+
875
+ |クライアント|
876
+ :JWTトークン保存;
877
+ :API呼び出し;
878
+ note right
879
+ Authorization: Bearer {token}
880
+ end note
881
+
882
+ |API Gateway|
883
+ :JWTトークン検証;
884
+ if (有効?) then (yes)
885
+ :クレーム抽出;
886
+ :認可チェック;
887
+ if (権限あり?) then (yes)
888
+ |バックエンドサービス|
889
+ :リクエスト処理;
890
+ :レスポンス返却;
891
+ else (no)
892
+ :403 Forbidden;
893
+ endif
894
+ else (no)
895
+ :401 Unauthorized;
896
+ endif
897
+
898
+ |クライアント|
899
+ :結果受信;
900
+
901
+ stop
902
+
903
+ @enduml
904
+ ```
905
+
906
+ <details>
907
+ <summary>Java 実装例(Spring Security + JWT)</summary>
908
+
909
+ ```java
910
+ // JWT フィルター
911
+ @Component
912
+ public class JwtAuthenticationFilter extends OncePerRequestFilter {
913
+ private final JwtTokenProvider tokenProvider;
914
+
915
+ @Override
916
+ protected void doFilterInternal(HttpServletRequest request,
917
+ HttpServletResponse response,
918
+ FilterChain filterChain)
919
+ throws ServletException, IOException {
920
+
921
+ String token = extractToken(request);
922
+
923
+ if (token != null && tokenProvider.validateToken(token)) {
924
+ Authentication auth = tokenProvider.getAuthentication(token);
925
+ SecurityContextHolder.getContext().setAuthentication(auth);
926
+ }
927
+
928
+ filterChain.doFilter(request, response);
929
+ }
930
+
931
+ private String extractToken(HttpServletRequest request) {
932
+ String bearerToken = request.getHeader("Authorization");
933
+ if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
934
+ return bearerToken.substring(7);
935
+ }
936
+ return null;
937
+ }
938
+ }
939
+
940
+ // JWT プロバイダー
941
+ @Component
942
+ public class JwtTokenProvider {
943
+ @Value("${jwt.secret}")
944
+ private String jwtSecret;
945
+
946
+ @Value("${jwt.expiration}")
947
+ private long jwtExpiration;
948
+
949
+ public String generateToken(Authentication authentication) {
950
+ UserDetails userDetails = (UserDetails) authentication.getPrincipal();
951
+
952
+ return Jwts.builder()
953
+ .setSubject(userDetails.getUsername())
954
+ .claim("roles", userDetails.getAuthorities().stream()
955
+ .map(GrantedAuthority::getAuthority)
956
+ .collect(Collectors.toList()))
957
+ .setIssuedAt(new Date())
958
+ .setExpiration(new Date(System.currentTimeMillis() + jwtExpiration))
959
+ .signWith(SignatureAlgorithm.HS512, jwtSecret)
960
+ .compact();
961
+ }
962
+
963
+ public boolean validateToken(String token) {
964
+ try {
965
+ Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token);
966
+ return true;
967
+ } catch (JwtException | IllegalArgumentException e) {
968
+ return false;
969
+ }
970
+ }
971
+
972
+ public Authentication getAuthentication(String token) {
973
+ Claims claims = Jwts.parser()
974
+ .setSigningKey(jwtSecret)
975
+ .parseClaimsJws(token)
976
+ .getBody();
977
+
978
+ List<String> roles = claims.get("roles", List.class);
979
+ List<GrantedAuthority> authorities = roles.stream()
980
+ .map(SimpleGrantedAuthority::new)
981
+ .collect(Collectors.toList());
982
+
983
+ User principal = new User(claims.getSubject(), "", authorities);
984
+ return new UsernamePasswordAuthenticationToken(
985
+ principal, token, authorities
986
+ );
987
+ }
988
+ }
989
+ ```
990
+
991
+ </details>
992
+
993
+ ### レート制限とスロットリング
994
+
995
+ ```plantuml
996
+ @startuml
997
+ title レート制限アルゴリズム
998
+
999
+ rectangle "Token Bucket" as token_bucket {
1000
+ note right
1001
+ ・バケットにトークンを蓄積
1002
+ ・リクエスト時にトークン消費
1003
+ ・バースト対応可能
1004
+
1005
+ 設定例:
1006
+ rate: 100/分
1007
+ burst: 20
1008
+ end note
1009
+ }
1010
+
1011
+ rectangle "Sliding Window" as sliding_window {
1012
+ note right
1013
+ ・時間枠内のリクエスト数をカウント
1014
+ ・枠をスライドして計算
1015
+ ・より正確な制限
1016
+
1017
+ 設定例:
1018
+ window: 1分
1019
+ limit: 100
1020
+ end note
1021
+ }
1022
+
1023
+ rectangle "Fixed Window" as fixed_window {
1024
+ note right
1025
+ ・固定時間枠でカウント
1026
+ ・シンプルな実装
1027
+ ・境界でバーストの可能性
1028
+
1029
+ 設定例:
1030
+ window: 1分
1031
+ limit: 100
1032
+ end note
1033
+ }
1034
+
1035
+ @enduml
1036
+ ```
1037
+
1038
+ <details>
1039
+ <summary>Spring Cloud Gateway 設定例</summary>
1040
+
1041
+ ```yaml
1042
+ # application.yml
1043
+ spring:
1044
+ cloud:
1045
+ gateway:
1046
+ routes:
1047
+ - id: sales-service
1048
+ uri: lb://sales-service
1049
+ predicates:
1050
+ - Path=/api/v1/sales/**
1051
+ filters:
1052
+ - name: RequestRateLimiter
1053
+ args:
1054
+ redis-rate-limiter.replenishRate: 100
1055
+ redis-rate-limiter.burstCapacity: 200
1056
+ key-resolver: "#{@userKeyResolver}"
1057
+ - name: CircuitBreaker
1058
+ args:
1059
+ name: salesCircuitBreaker
1060
+ fallbackUri: forward:/fallback/sales
1061
+
1062
+ - id: accounting-service
1063
+ uri: lb://accounting-service
1064
+ predicates:
1065
+ - Path=/api/v1/accounting/**
1066
+ filters:
1067
+ - name: RequestRateLimiter
1068
+ args:
1069
+ redis-rate-limiter.replenishRate: 50
1070
+ redis-rate-limiter.burstCapacity: 100
1071
+
1072
+ default-filters:
1073
+ - name: Retry
1074
+ args:
1075
+ retries: 3
1076
+ statuses: BAD_GATEWAY,SERVICE_UNAVAILABLE
1077
+ methods: GET
1078
+ backoff:
1079
+ firstBackoff: 100ms
1080
+ maxBackoff: 500ms
1081
+ factor: 2
1082
+ ```
1083
+
1084
+ ```java
1085
+ // Key Resolver(ユーザー単位のレート制限)
1086
+ @Configuration
1087
+ public class RateLimiterConfig {
1088
+
1089
+ @Bean
1090
+ public KeyResolver userKeyResolver() {
1091
+ return exchange -> {
1092
+ String userId = exchange.getRequest()
1093
+ .getHeaders()
1094
+ .getFirst("X-User-Id");
1095
+ return Mono.just(userId != null ? userId : "anonymous");
1096
+ };
1097
+ }
1098
+
1099
+ @Bean
1100
+ public KeyResolver apiKeyResolver() {
1101
+ return exchange -> {
1102
+ String apiKey = exchange.getRequest()
1103
+ .getHeaders()
1104
+ .getFirst("X-API-Key");
1105
+ return Mono.just(apiKey != null ? apiKey : "default");
1106
+ };
1107
+ }
1108
+ }
1109
+ ```
1110
+
1111
+ </details>
1112
+
1113
+ ### ログ集約とモニタリング
1114
+
1115
+ ```plantuml
1116
+ @startuml
1117
+ title 分散トレーシングとログ集約
1118
+
1119
+ rectangle "クライアント" as client
1120
+
1121
+ rectangle "API Gateway" as gateway {
1122
+ rectangle "トレースID生成" as trace_gen
1123
+ }
1124
+
1125
+ rectangle "サービス群" as services {
1126
+ rectangle "販売サービス" as sales
1127
+ rectangle "在庫サービス" as inventory
1128
+ rectangle "会計サービス" as accounting
1129
+ }
1130
+
1131
+ rectangle "観測基盤" as observability {
1132
+ database "ログ集約\n(ELK/Loki)" as logs
1133
+ database "メトリクス\n(Prometheus)" as metrics
1134
+ database "トレース\n(Jaeger/Zipkin)" as traces
1135
+ }
1136
+
1137
+ rectangle "ダッシュボード" as dashboard {
1138
+ rectangle "Grafana" as grafana
1139
+ rectangle "Kibana" as kibana
1140
+ }
1141
+
1142
+ client --> gateway : リクエスト
1143
+ gateway --> sales : traceId
1144
+ sales --> inventory : traceId
1145
+ sales --> accounting : traceId
1146
+
1147
+ gateway --> logs
1148
+ sales --> logs
1149
+ inventory --> logs
1150
+ accounting --> logs
1151
+
1152
+ gateway --> metrics
1153
+ sales --> metrics
1154
+ inventory --> metrics
1155
+ accounting --> metrics
1156
+
1157
+ gateway --> traces
1158
+ sales --> traces
1159
+ inventory --> traces
1160
+ accounting --> traces
1161
+
1162
+ logs --> kibana
1163
+ metrics --> grafana
1164
+ traces --> grafana
1165
+
1166
+ @enduml
1167
+ ```
1168
+
1169
+ <details>
1170
+ <summary>Java 実装例(Micrometer + OpenTelemetry)</summary>
1171
+
1172
+ ```java
1173
+ // トレーシング設定
1174
+ @Configuration
1175
+ public class TracingConfig {
1176
+
1177
+ @Bean
1178
+ public Tracer tracer() {
1179
+ return GlobalOpenTelemetry.getTracer("sales-service");
1180
+ }
1181
+ }
1182
+
1183
+ // カスタムメトリクス
1184
+ @Component
1185
+ public class OrderMetrics {
1186
+ private final MeterRegistry meterRegistry;
1187
+ private final Counter orderCreatedCounter;
1188
+ private final Timer orderProcessingTimer;
1189
+
1190
+ public OrderMetrics(MeterRegistry meterRegistry) {
1191
+ this.meterRegistry = meterRegistry;
1192
+
1193
+ this.orderCreatedCounter = Counter.builder("orders.created")
1194
+ .description("Number of orders created")
1195
+ .tag("service", "sales")
1196
+ .register(meterRegistry);
1197
+
1198
+ this.orderProcessingTimer = Timer.builder("orders.processing.time")
1199
+ .description("Order processing time")
1200
+ .tag("service", "sales")
1201
+ .register(meterRegistry);
1202
+ }
1203
+
1204
+ public void recordOrderCreated(String status) {
1205
+ orderCreatedCounter.increment();
1206
+ meterRegistry.counter("orders.created.by.status",
1207
+ "status", status).increment();
1208
+ }
1209
+
1210
+ public void recordProcessingTime(long durationMs) {
1211
+ orderProcessingTimer.record(Duration.ofMillis(durationMs));
1212
+ }
1213
+ }
1214
+
1215
+ // 構造化ログ
1216
+ @Slf4j
1217
+ @Service
1218
+ public class OrderService {
1219
+
1220
+ public Order createOrder(CreateOrderRequest request) {
1221
+ MDC.put("orderId", request.getOrderId());
1222
+ MDC.put("customerId", request.getCustomerId());
1223
+
1224
+ try {
1225
+ log.info("Creating order", kv("action", "create_order_start"));
1226
+
1227
+ Order order = processOrder(request);
1228
+
1229
+ log.info("Order created successfully",
1230
+ kv("action", "create_order_complete"),
1231
+ kv("totalAmount", order.getTotalAmount()));
1232
+
1233
+ return order;
1234
+ } catch (Exception e) {
1235
+ log.error("Failed to create order",
1236
+ kv("action", "create_order_failed"),
1237
+ kv("error", e.getMessage()), e);
1238
+ throw e;
1239
+ } finally {
1240
+ MDC.clear();
1241
+ }
1242
+ }
1243
+ }
1244
+ ```
1245
+
1246
+ </details>
1247
+
1248
+ ---
1249
+
1250
+ ## 38.4 API インテグレーションテスト
1251
+
1252
+ ### テストコンテナによる統合テスト環境
1253
+
1254
+ ```plantuml
1255
+ @startuml
1256
+ title テストコンテナアーキテクチャ
1257
+
1258
+ rectangle "テスト実行環境" as test_env {
1259
+ rectangle "JUnit 5" as junit
1260
+ rectangle "テストクラス" as test_class
1261
+ }
1262
+
1263
+ rectangle "Testcontainers" as testcontainers {
1264
+ rectangle "PostgreSQL\nコンテナ" as postgres
1265
+ rectangle "RabbitMQ\nコンテナ" as rabbitmq
1266
+ rectangle "Redis\nコンテナ" as redis
1267
+ }
1268
+
1269
+ rectangle "アプリケーション" as app {
1270
+ rectangle "Spring Boot\nアプリケーション" as spring
1271
+ }
1272
+
1273
+ test_class --> spring : HTTP リクエスト
1274
+ spring --> postgres : データアクセス
1275
+ spring --> rabbitmq : メッセージング
1276
+ spring --> redis : キャッシュ
1277
+
1278
+ junit --> testcontainers : コンテナ起動
1279
+ testcontainers --> postgres
1280
+ testcontainers --> rabbitmq
1281
+ testcontainers --> redis
1282
+
1283
+ note bottom of testcontainers
1284
+ 【Testcontainersの利点】
1285
+ ・本番同等の環境でテスト
1286
+ ・テスト間の分離
1287
+ ・CI/CDでの再現性
1288
+ ・自動クリーンアップ
1289
+ end note
1290
+
1291
+ @enduml
1292
+ ```
1293
+
1294
+ <details>
1295
+ <summary>Java 実装例</summary>
1296
+
1297
+ ```java
1298
+ // テストコンテナ設定
1299
+ @Testcontainers
1300
+ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
1301
+ @ActiveProfiles("test")
1302
+ public abstract class IntegrationTestBase {
1303
+
1304
+ @Container
1305
+ static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
1306
+ .withDatabaseName("testdb")
1307
+ .withUsername("test")
1308
+ .withPassword("test");
1309
+
1310
+ @Container
1311
+ static RabbitMQContainer rabbitmq = new RabbitMQContainer("rabbitmq:3.12-management");
1312
+
1313
+ @DynamicPropertySource
1314
+ static void configureProperties(DynamicPropertyRegistry registry) {
1315
+ registry.add("spring.datasource.url", postgres::getJdbcUrl);
1316
+ registry.add("spring.datasource.username", postgres::getUsername);
1317
+ registry.add("spring.datasource.password", postgres::getPassword);
1318
+ registry.add("spring.rabbitmq.host", rabbitmq::getHost);
1319
+ registry.add("spring.rabbitmq.port", rabbitmq::getAmqpPort);
1320
+ }
1321
+
1322
+ @Autowired
1323
+ protected TestRestTemplate restTemplate;
1324
+
1325
+ @Autowired
1326
+ protected JdbcTemplate jdbcTemplate;
1327
+
1328
+ @BeforeEach
1329
+ void setUp() {
1330
+ // テストデータのクリーンアップ
1331
+ cleanupTestData();
1332
+ }
1333
+
1334
+ protected void cleanupTestData() {
1335
+ jdbcTemplate.execute("TRUNCATE TABLE 受注明細 CASCADE");
1336
+ jdbcTemplate.execute("TRUNCATE TABLE 受注 CASCADE");
1337
+ }
1338
+ }
1339
+ ```
1340
+
1341
+ </details>
1342
+
1343
+ ### REST API エンドポイントのテスト
1344
+
1345
+ ```plantuml
1346
+ @startuml
1347
+ title API テストの構造
1348
+
1349
+ ' ノード間の間隔を調整
1350
+ skinparam nodesep 50
1351
+ skinparam ranksep 50
1352
+
1353
+ rectangle "== Given(前提条件)\n\n・テストデータ準備\n・認証トークン取得\n・モックの設定" as given
1354
+
1355
+ rectangle "== When(操作)\n\n・APIエンドポイント呼び出し\n・リクエストボディ設定\n・ヘッダー設定" as when
1356
+
1357
+ rectangle "== Then(検証)\n\n・ステータスコード確認\n・レスポンスボディ検証\n・データベース状態確認\n・イベント発行確認" as then
1358
+
1359
+ given --> when
1360
+ when --> then
1361
+
1362
+ @enduml
1363
+ ```
1364
+
1365
+ <details>
1366
+ <summary>Java 実装例</summary>
1367
+
1368
+ ```java
1369
+ // 受注API統合テスト
1370
+ class OrderApiIntegrationTest extends IntegrationTestBase {
1371
+
1372
+ @Autowired
1373
+ private CustomerRepository customerRepository;
1374
+
1375
+ @Autowired
1376
+ private ProductRepository productRepository;
1377
+
1378
+ private String authToken;
1379
+ private Customer testCustomer;
1380
+ private Product testProduct;
1381
+
1382
+ @BeforeEach
1383
+ void setUpTestData() {
1384
+ // 認証トークン取得
1385
+ authToken = getAuthToken("test-user", "password");
1386
+
1387
+ // テストデータ準備
1388
+ testCustomer = customerRepository.save(
1389
+ new Customer("CUS-001", "テスト顧客", "test@example.com")
1390
+ );
1391
+ testProduct = productRepository.save(
1392
+ new Product("PRD-001", "テスト商品", new BigDecimal("1000"))
1393
+ );
1394
+ }
1395
+
1396
+ @Test
1397
+ @DisplayName("受注登録: 正常系")
1398
+ void createOrder_Success() {
1399
+ // Given
1400
+ CreateOrderRequest request = CreateOrderRequest.builder()
1401
+ .customerId(testCustomer.getCustomerId())
1402
+ .orderDate(LocalDate.now())
1403
+ .lines(List.of(
1404
+ OrderLineRequest.builder()
1405
+ .productId(testProduct.getProductId())
1406
+ .quantity(10)
1407
+ .build()
1408
+ ))
1409
+ .build();
1410
+
1411
+ // When
1412
+ ResponseEntity<OrderResponse> response = restTemplate.exchange(
1413
+ "/api/v1/sales/orders",
1414
+ HttpMethod.POST,
1415
+ new HttpEntity<>(request, createAuthHeaders()),
1416
+ OrderResponse.class
1417
+ );
1418
+
1419
+ // Then
1420
+ assertThat(response.getStatusCode()).isEqualTo(HttpStatus.CREATED);
1421
+ assertThat(response.getBody()).isNotNull();
1422
+ assertThat(response.getBody().getOrderId()).isNotBlank();
1423
+ assertThat(response.getBody().getStatus()).isEqualTo("DRAFT");
1424
+ assertThat(response.getBody().getTotalAmount())
1425
+ .isEqualByComparingTo(new BigDecimal("10000"));
1426
+
1427
+ // データベース状態確認
1428
+ Order savedOrder = orderRepository.findById(
1429
+ response.getBody().getOrderId()
1430
+ ).orElseThrow();
1431
+ assertThat(savedOrder.getCustomerId()).isEqualTo(testCustomer.getCustomerId());
1432
+ assertThat(savedOrder.getLines()).hasSize(1);
1433
+ }
1434
+
1435
+ @Test
1436
+ @DisplayName("受注登録: バリデーションエラー")
1437
+ void createOrder_ValidationError() {
1438
+ // Given - 明細なしのリクエスト
1439
+ CreateOrderRequest request = CreateOrderRequest.builder()
1440
+ .customerId(testCustomer.getCustomerId())
1441
+ .orderDate(LocalDate.now())
1442
+ .lines(List.of()) // 空の明細
1443
+ .build();
1444
+
1445
+ // When
1446
+ ResponseEntity<ProblemDetail> response = restTemplate.exchange(
1447
+ "/api/v1/sales/orders",
1448
+ HttpMethod.POST,
1449
+ new HttpEntity<>(request, createAuthHeaders()),
1450
+ ProblemDetail.class
1451
+ );
1452
+
1453
+ // Then
1454
+ assertThat(response.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
1455
+ assertThat(response.getBody().getTitle()).isEqualTo("Validation Error");
1456
+ }
1457
+
1458
+ @Test
1459
+ @DisplayName("受注一覧取得: ページング")
1460
+ void getOrders_Paging() {
1461
+ // Given - 複数の受注を作成
1462
+ for (int i = 0; i < 25; i++) {
1463
+ createTestOrder("ORD-" + String.format("%03d", i));
1464
+ }
1465
+
1466
+ // When
1467
+ ResponseEntity<PagedResponse<OrderSummary>> response = restTemplate.exchange(
1468
+ "/api/v1/sales/orders?page=0&size=10",
1469
+ HttpMethod.GET,
1470
+ new HttpEntity<>(createAuthHeaders()),
1471
+ new ParameterizedTypeReference<>() {}
1472
+ );
1473
+
1474
+ // Then
1475
+ assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
1476
+ assertThat(response.getBody().getContent()).hasSize(10);
1477
+ assertThat(response.getBody().getTotalElements()).isEqualTo(25);
1478
+ assertThat(response.getBody().getTotalPages()).isEqualTo(3);
1479
+ }
1480
+
1481
+ private HttpHeaders createAuthHeaders() {
1482
+ HttpHeaders headers = new HttpHeaders();
1483
+ headers.setBearerAuth(authToken);
1484
+ headers.setContentType(MediaType.APPLICATION_JSON);
1485
+ return headers;
1486
+ }
1487
+ }
1488
+ ```
1489
+
1490
+ </details>
1491
+
1492
+ ### サービス間連携テスト
1493
+
1494
+ ```plantuml
1495
+ @startuml
1496
+ title サービス間連携テストのアプローチ
1497
+
1498
+ ' ボックスの定義
1499
+ rectangle "Contract Testing" as contract
1500
+ note right of contract
1501
+ ・Pact / Spring Cloud Contract
1502
+ ・プロバイダーとコンシューマーの契約
1503
+ ・独立したテスト実行
1504
+ end note
1505
+
1506
+ rectangle "Component Testing" as component
1507
+ note right of component
1508
+ ・WireMockで外部サービスをモック
1509
+ ・単一サービスの統合テスト
1510
+ ・高速なフィードバック
1511
+ end note
1512
+
1513
+ rectangle "End-to-End Testing" as e2e
1514
+ note right of e2e
1515
+ ・全サービスを起動
1516
+ ・Docker Composeで環境構築
1517
+ ・本番同等のシナリオテスト
1518
+ end note
1519
+
1520
+ ' 配置(上から下に並べる)
1521
+ contract -[hidden]down-> component
1522
+ component -[hidden]down-> e2e
1523
+
1524
+ @enduml
1525
+ ```
1526
+
1527
+ <details>
1528
+ <summary>Java 実装例(WireMock)</summary>
1529
+
1530
+ ```java
1531
+ // WireMockを使用したサービス間連携テスト
1532
+ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
1533
+ @AutoConfigureWireMock(port = 0)
1534
+ class OrderServiceWithInventoryTest {
1535
+
1536
+ @Autowired
1537
+ private TestRestTemplate restTemplate;
1538
+
1539
+ @Value("${wiremock.server.port}")
1540
+ private int wireMockPort;
1541
+
1542
+ @BeforeEach
1543
+ void setUp() {
1544
+ // 在庫サービスのモック設定
1545
+ stubFor(get(urlPathMatching("/api/v1/inventory/.*"))
1546
+ .willReturn(aResponse()
1547
+ .withStatus(200)
1548
+ .withHeader("Content-Type", "application/json")
1549
+ .withBody("""
1550
+ {
1551
+ "productId": "PRD-001",
1552
+ "quantity": 100,
1553
+ "allocatedQuantity": 0,
1554
+ "availableQuantity": 100
1555
+ }
1556
+ """)));
1557
+
1558
+ stubFor(post(urlPathMatching("/api/v1/inventory/allocate"))
1559
+ .willReturn(aResponse()
1560
+ .withStatus(200)
1561
+ .withHeader("Content-Type", "application/json")
1562
+ .withBody("""
1563
+ {
1564
+ "success": true,
1565
+ "allocationId": "ALLOC-001"
1566
+ }
1567
+ """)));
1568
+ }
1569
+
1570
+ @Test
1571
+ @DisplayName("受注確定: 在庫引当成功")
1572
+ void confirmOrder_InventoryAllocated() {
1573
+ // Given
1574
+ String orderId = createDraftOrder();
1575
+
1576
+ // When
1577
+ ResponseEntity<OrderResponse> response = restTemplate.exchange(
1578
+ "/api/v1/sales/orders/{orderId}/confirm",
1579
+ HttpMethod.POST,
1580
+ new HttpEntity<>(createAuthHeaders()),
1581
+ OrderResponse.class,
1582
+ orderId
1583
+ );
1584
+
1585
+ // Then
1586
+ assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
1587
+ assertThat(response.getBody().getStatus()).isEqualTo("CONFIRMED");
1588
+
1589
+ // 在庫サービスへの呼び出しを検証
1590
+ verify(postRequestedFor(urlPathEqualTo("/api/v1/inventory/allocate"))
1591
+ .withRequestBody(matchingJsonPath("$.productId", equalTo("PRD-001")))
1592
+ .withRequestBody(matchingJsonPath("$.quantity", equalTo("10"))));
1593
+ }
1594
+
1595
+ @Test
1596
+ @DisplayName("受注確定: 在庫不足")
1597
+ void confirmOrder_InsufficientStock() {
1598
+ // Given - 在庫不足のレスポンスを設定
1599
+ stubFor(post(urlPathMatching("/api/v1/inventory/allocate"))
1600
+ .willReturn(aResponse()
1601
+ .withStatus(200)
1602
+ .withBody("""
1603
+ {
1604
+ "success": false,
1605
+ "errorMessage": "Insufficient stock"
1606
+ }
1607
+ """)));
1608
+
1609
+ String orderId = createDraftOrder();
1610
+
1611
+ // When
1612
+ ResponseEntity<ProblemDetail> response = restTemplate.exchange(
1613
+ "/api/v1/sales/orders/{orderId}/confirm",
1614
+ HttpMethod.POST,
1615
+ new HttpEntity<>(createAuthHeaders()),
1616
+ ProblemDetail.class,
1617
+ orderId
1618
+ );
1619
+
1620
+ // Then
1621
+ assertThat(response.getStatusCode()).isEqualTo(HttpStatus.CONFLICT);
1622
+ assertThat(response.getBody().getDetail()).contains("在庫不足");
1623
+ }
1624
+
1625
+ @Test
1626
+ @DisplayName("受注確定: 在庫サービスタイムアウト")
1627
+ void confirmOrder_InventoryServiceTimeout() {
1628
+ // Given - タイムアウトをシミュレート
1629
+ stubFor(post(urlPathMatching("/api/v1/inventory/allocate"))
1630
+ .willReturn(aResponse()
1631
+ .withFixedDelay(5000) // 5秒遅延
1632
+ .withStatus(200)));
1633
+
1634
+ String orderId = createDraftOrder();
1635
+
1636
+ // When
1637
+ ResponseEntity<ProblemDetail> response = restTemplate.exchange(
1638
+ "/api/v1/sales/orders/{orderId}/confirm",
1639
+ HttpMethod.POST,
1640
+ new HttpEntity<>(createAuthHeaders()),
1641
+ ProblemDetail.class,
1642
+ orderId
1643
+ );
1644
+
1645
+ // Then - サーキットブレーカーによるフォールバック
1646
+ assertThat(response.getStatusCode()).isEqualTo(HttpStatus.SERVICE_UNAVAILABLE);
1647
+ }
1648
+ }
1649
+ ```
1650
+
1651
+ </details>
1652
+
1653
+ ---
1654
+
1655
+ ## 38.5 まとめ
1656
+
1657
+ 本章では、API 設計とサービス連携のパターンについて解説しました。
1658
+
1659
+ ### 学んだこと
1660
+
1661
+ 1. **API 設計の原則**
1662
+
1663
+ - RESTful API の制約と設計原則
1664
+ - リソース指向設計(名詞ベース、HTTP メソッド)
1665
+ - バージョニング戦略(URI パス推奨)
1666
+
1667
+ 2. **サービス間通信**
1668
+
1669
+ - 同期通信(REST、gRPC)の使い分け
1670
+ - 非同期通信(メッセージキュー)
1671
+ - サーキットブレーカーによる障害対策
1672
+
1673
+ 3. **API ゲートウェイ**
1674
+
1675
+ - 認証・認可の一元化(JWT)
1676
+ - レート制限とスロットリング
1677
+ - ログ集約とモニタリング
1678
+
1679
+ 4. **API インテグレーションテスト**
1680
+
1681
+ - テストコンテナによる統合テスト環境
1682
+ - REST API エンドポイントのテスト
1683
+ - サービス間連携テスト(WireMock)
1684
+
1685
+ ### API 設計チェックリスト
1686
+
1687
+ - [ ] リソースは名詞(複数形)で命名されているか
1688
+ - [ ] HTTP メソッドが正しく使用されているか
1689
+ - [ ] エラーレスポンスは Problem Detail 形式か
1690
+ - [ ] バージョニング戦略が決定されているか
1691
+ - [ ] 認証・認可が適切に実装されているか
1692
+ - [ ] レート制限が設定されているか
1693
+ - [ ] サーキットブレーカーが導入されているか
1694
+ - [ ] 統合テストが整備されているか
1695
+
1696
+ ### 次章の予告
1697
+
1698
+ 第39章では、データ連携の実装パターンについて解説します。バッチ連携、リアルタイム連携、連携テーブルの設計など、具体的な実装パターンを学びます。