@k2works/claude-code-booster 3.5.0 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +42 -42
- package/bin/claude-code-booster +90 -90
- package/lib/assets/.claude/README.md +239 -239
- package/lib/assets/.claude/scripts/generate-inception-deck.mjs +911 -911
- package/lib/assets/.claude/settings.json +11 -11
- package/lib/assets/.claude/skills/ai-agent-guidelines/SKILL.md +111 -111
- package/lib/assets/.claude/skills/analyzing-architecture/SKILL.md +83 -83
- package/lib/assets/.claude/skills/analyzing-business/SKILL.md +95 -95
- package/lib/assets/.claude/skills/analyzing-data-model/SKILL.md +77 -77
- package/lib/assets/.claude/skills/analyzing-domain-model/SKILL.md +117 -88
- package/lib/assets/.claude/skills/analyzing-inception-deck/SKILL.md +84 -84
- package/lib/assets/.claude/skills/analyzing-non-functional/SKILL.md +95 -95
- package/lib/assets/.claude/skills/analyzing-operation/SKILL.md +95 -95
- package/lib/assets/.claude/skills/analyzing-requirements/SKILL.md +91 -91
- package/lib/assets/.claude/skills/analyzing-tech-stack/SKILL.md +101 -101
- package/lib/assets/.claude/skills/analyzing-test-strategy/SKILL.md +89 -89
- package/lib/assets/.claude/skills/analyzing-ui-design/SKILL.md +80 -80
- package/lib/assets/.claude/skills/analyzing-usecases/SKILL.md +72 -72
- package/lib/assets/.claude/skills/creating-adr/SKILL.md +113 -113
- package/lib/assets/.claude/skills/developing-backend/SKILL.md +100 -100
- package/lib/assets/.claude/skills/developing-frontend/SKILL.md +93 -93
- package/lib/assets/.claude/skills/developing-release/SKILL.md +120 -120
- package/lib/assets/.claude/skills/generating-slides/SKILL.md +94 -94
- package/lib/assets/.claude/skills/git-commit/SKILL.md +81 -81
- package/lib/assets/.claude/skills/killing-processes/SKILL.md +44 -44
- package/lib/assets/.claude/skills/operating-backup/SKILL.md +59 -59
- package/lib/assets/.claude/skills/operating-cicd/SKILL.md +54 -54
- package/lib/assets/.claude/skills/operating-deploy/SKILL.md +67 -67
- package/lib/assets/.claude/skills/operating-docs/SKILL.md +219 -219
- package/lib/assets/.claude/skills/operating-provision/SKILL.md +77 -77
- package/lib/assets/.claude/skills/operating-setup/SKILL.md +63 -63
- package/lib/assets/.claude/skills/orchestrating-analysis/SKILL.md +104 -104
- package/lib/assets/.claude/skills/orchestrating-development/SKILL.md +162 -161
- package/lib/assets/.claude/skills/orchestrating-operation/SKILL.md +158 -158
- package/lib/assets/.claude/skills/orchestrating-project/SKILL.md +144 -144
- package/lib/assets/.claude/skills/planning-releases/SKILL.md +119 -119
- package/lib/assets/.claude/skills/syncing-github-project/SKILL.md +151 -151
- package/lib/assets/.claude/skills/tracking-progress/SKILL.md +91 -91
- package/lib/assets/.claude/skills/validating-iteration-plan/SKILL.md +215 -215
- package/lib/assets/.devcontainer/devcontainer.json +34 -34
- package/lib/assets/.env.example +17 -17
- package/lib/assets/.gitattributes +4 -4
- package/lib/assets/.github/workflows/docker-publish.yml +77 -77
- package/lib/assets/.github/workflows/mkdocs.yml +39 -39
- package/lib/assets/AGENTS.md +94 -94
- package/lib/assets/CLAUDE.md +183 -183
- package/lib/assets/README.md +254 -254
- package/lib/assets/docker-compose.yml +33 -33
- package/lib/assets/docs/adr/index.md +10 -10
- package/lib/assets/docs/article/functional-desgin-ppp/all/01-immutability-and-data-transformation.md +475 -475
- package/lib/assets/docs/article/functional-desgin-ppp/all/02-function-composition.md +519 -519
- package/lib/assets/docs/article/functional-desgin-ppp/all/03-polymorphism.md +537 -537
- package/lib/assets/docs/article/functional-desgin-ppp/all/04-data-validation.md +300 -300
- package/lib/assets/docs/article/functional-desgin-ppp/all/05-property-based-testing.md +320 -320
- package/lib/assets/docs/article/functional-desgin-ppp/all/06-tdd-and-functional.md +498 -498
- package/lib/assets/docs/article/functional-desgin-ppp/all/07-composite-pattern.md +298 -298
- package/lib/assets/docs/article/functional-desgin-ppp/all/08-decorator-pattern.md +291 -291
- package/lib/assets/docs/article/functional-desgin-ppp/all/09-adapter-pattern.md +336 -336
- package/lib/assets/docs/article/functional-desgin-ppp/all/10-strategy-pattern.md +303 -303
- package/lib/assets/docs/article/functional-desgin-ppp/all/11-command-pattern.md +286 -286
- package/lib/assets/docs/article/functional-desgin-ppp/all/12-visitor-pattern.md +322 -322
- package/lib/assets/docs/article/functional-desgin-ppp/all/13-abstract-factory-pattern.md +319 -319
- package/lib/assets/docs/article/functional-desgin-ppp/all/14-abstract-server-pattern.md +365 -365
- package/lib/assets/docs/article/functional-desgin-ppp/all/15-gossiping-bus-drivers.md +156 -156
- package/lib/assets/docs/article/functional-desgin-ppp/all/16-payroll-system.md +178 -178
- package/lib/assets/docs/article/functional-desgin-ppp/all/17-video-rental-system.md +312 -312
- package/lib/assets/docs/article/functional-desgin-ppp/all/18-concurrency-system.md +287 -287
- package/lib/assets/docs/article/functional-desgin-ppp/all/19-wa-tor-simulation.md +286 -286
- package/lib/assets/docs/article/functional-desgin-ppp/all/20-pattern-interactions.md +274 -274
- package/lib/assets/docs/article/functional-desgin-ppp/all/21-best-practices.md +294 -294
- package/lib/assets/docs/article/functional-desgin-ppp/all/22-oo-to-fp-migration.md +337 -337
- package/lib/assets/docs/article/functional-desgin-ppp/all/index.md +388 -388
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/01-immutability-and-data-transformation.md +273 -273
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/02-function-composition.md +380 -380
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/03-polymorphism.md +384 -384
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/04-clojure-spec.md +350 -350
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/05-property-based-testing.md +352 -352
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/06-tdd-in-functional.md +383 -383
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/07-composite-pattern.md +529 -529
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/08-decorator-pattern.md +395 -395
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/09-adapter-pattern.md +399 -399
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/10-strategy-pattern.md +485 -485
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/11-command-pattern.md +566 -566
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/12-visitor-pattern.md +567 -567
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/13-abstract-factory-pattern.md +475 -475
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/14-abstract-server-pattern.md +462 -462
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/15-gossiping-bus-drivers.md +325 -325
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/16-payroll-system.md +401 -401
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/17-video-rental-system.md +450 -450
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/18-concurrency-system.md +475 -475
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/19-wator-simulation.md +739 -739
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/20-pattern-interactions.md +567 -567
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/21-best-practices.md +518 -518
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/22-oo-to-fp-migration.md +532 -532
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/index.md +241 -241
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/01-immutability-and-data-transformation.md +383 -383
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/02-function-composition.md +374 -374
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/03-polymorphism.md +375 -375
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/04-data-validation.md +195 -195
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/05-property-based-testing.md +268 -268
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/06-tdd-and-fp.md +294 -294
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/07-effects-and-pure-functions.md +164 -164
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/08-error-handling-strategies.md +168 -168
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/09-io-and-external-systems.md +254 -254
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/10-concurrency-patterns.md +269 -269
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/11-command-pattern.md +148 -148
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/12-visitor-pattern.md +176 -176
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/13-abstract-factory-pattern.md +604 -604
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/14-abstract-server-pattern.md +729 -729
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/15-gossiping-bus-drivers.md +291 -291
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/16-payroll-system.md +420 -420
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/17-video-rental-system.md +319 -319
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/18-concurrency-system.md +466 -466
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/19-wator-simulation.md +523 -523
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/20-pattern-interactions.md +287 -287
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/21-best-practices.md +340 -340
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/22-oo-to-fp-migration.md +395 -395
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/index.md +248 -248
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/01-immutability-and-data-transformation.md +384 -384
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/02-function-composition.md +452 -452
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/03-polymorphism.md +495 -495
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/04-data-validation.md +416 -416
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/05-property-based-testing.md +382 -382
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/06-tdd-functional.md +687 -687
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/07-composite-pattern.md +442 -442
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/08-decorator-pattern.md +479 -479
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/09-adapter-pattern.md +479 -479
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/10-strategy-pattern.md +427 -427
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/11-command-pattern.md +428 -428
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/12-visitor-pattern.md +339 -339
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/13-abstract-factory-pattern.md +309 -309
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/14-abstract-server-pattern.md +596 -596
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/15-gossiping-bus-drivers.md +355 -355
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/16-payroll-system.md +350 -350
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/17-video-rental-system.md +414 -414
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/18-concurrency-system.md +367 -367
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/19-wator-simulation.md +403 -403
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/20-pattern-interactions.md +291 -291
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/21-best-practices.md +324 -324
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/22-oo-to-fp-migration.md +332 -332
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/index.md +274 -274
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/01-immutability-and-data-transformation.md +298 -298
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/02-function-composition.md +304 -304
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/03-polymorphism.md +362 -362
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/04-data-validation.md +257 -257
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/05-property-based-testing.md +254 -254
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/06-tdd-functional.md +283 -283
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/07-composite-pattern.md +395 -395
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/08-decorator-pattern.md +319 -319
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/09-adapter-pattern.md +382 -382
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/10-strategy-pattern.md +287 -287
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/11-command-pattern.md +303 -303
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/12-visitor-pattern.md +326 -326
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/13-abstract-factory-pattern.md +332 -332
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/14-abstract-server-pattern.md +379 -379
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/15-gossiping-bus-drivers.md +177 -177
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/16-payroll-system.md +219 -219
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/17-video-rental-system.md +244 -244
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/18-concurrency-system.md +363 -363
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/19-wator-simulation.md +438 -438
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/20-pattern-interactions.md +325 -325
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/21-best-practices.md +403 -403
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/22-oo-to-fp-migration.md +469 -469
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/index.md +174 -174
- package/lib/assets/docs/article/functional-desgin-ppp/index.md +90 -90
- package/lib/assets/docs/article/functional-desgin-ppp/rust/01-immutability-and-data-transformation.md +450 -450
- package/lib/assets/docs/article/functional-desgin-ppp/rust/02-function-composition.md +463 -463
- package/lib/assets/docs/article/functional-desgin-ppp/rust/03-polymorphism.md +425 -425
- package/lib/assets/docs/article/functional-desgin-ppp/rust/04-data-validation.md +273 -273
- package/lib/assets/docs/article/functional-desgin-ppp/rust/05-property-based-testing.md +247 -247
- package/lib/assets/docs/article/functional-desgin-ppp/rust/06-tdd-and-functional.md +841 -841
- package/lib/assets/docs/article/functional-desgin-ppp/rust/07-composite-pattern.md +384 -384
- package/lib/assets/docs/article/functional-desgin-ppp/rust/08-decorator-pattern.md +383 -383
- package/lib/assets/docs/article/functional-desgin-ppp/rust/09-adapter-pattern.md +339 -339
- package/lib/assets/docs/article/functional-desgin-ppp/rust/10-strategy-pattern.md +331 -331
- package/lib/assets/docs/article/functional-desgin-ppp/rust/11-command-pattern.md +356 -356
- package/lib/assets/docs/article/functional-desgin-ppp/rust/12-visitor-pattern.md +379 -379
- package/lib/assets/docs/article/functional-desgin-ppp/rust/13-abstract-factory-pattern.md +361 -361
- package/lib/assets/docs/article/functional-desgin-ppp/rust/14-abstract-server-pattern.md +392 -392
- package/lib/assets/docs/article/functional-desgin-ppp/rust/15-gossiping-bus-drivers.md +300 -300
- package/lib/assets/docs/article/functional-desgin-ppp/rust/16-payroll-system.md +297 -297
- package/lib/assets/docs/article/functional-desgin-ppp/rust/17-video-rental-system.md +304 -304
- package/lib/assets/docs/article/functional-desgin-ppp/rust/18-concurrency-system.md +315 -315
- package/lib/assets/docs/article/functional-desgin-ppp/rust/19-wator-simulation.md +311 -311
- package/lib/assets/docs/article/functional-desgin-ppp/rust/20-pattern-interactions.md +304 -304
- package/lib/assets/docs/article/functional-desgin-ppp/rust/21-best-practices.md +336 -336
- package/lib/assets/docs/article/functional-desgin-ppp/rust/22-oo-to-fp-migration.md +349 -349
- package/lib/assets/docs/article/functional-desgin-ppp/rust/index.md +243 -243
- package/lib/assets/docs/article/functional-desgin-ppp/scala/01-immutability-and-data-transformation.md +328 -328
- package/lib/assets/docs/article/functional-desgin-ppp/scala/02-function-composition.md +348 -348
- package/lib/assets/docs/article/functional-desgin-ppp/scala/03-polymorphism.md +357 -357
- package/lib/assets/docs/article/functional-desgin-ppp/scala/04-data-validation.md +364 -364
- package/lib/assets/docs/article/functional-desgin-ppp/scala/05-property-based-testing.md +515 -515
- package/lib/assets/docs/article/functional-desgin-ppp/scala/06-tdd-functional.md +557 -557
- package/lib/assets/docs/article/functional-desgin-ppp/scala/07-composite-pattern.md +363 -363
- package/lib/assets/docs/article/functional-desgin-ppp/scala/08-decorator-pattern.md +327 -327
- package/lib/assets/docs/article/functional-desgin-ppp/scala/09-adapter-pattern.md +517 -517
- package/lib/assets/docs/article/functional-desgin-ppp/scala/10-strategy-pattern.md +441 -441
- package/lib/assets/docs/article/functional-desgin-ppp/scala/11-command-pattern.md +407 -407
- package/lib/assets/docs/article/functional-desgin-ppp/scala/12-visitor-pattern.md +379 -379
- package/lib/assets/docs/article/functional-desgin-ppp/scala/13-abstract-factory-pattern.md +398 -398
- package/lib/assets/docs/article/functional-desgin-ppp/scala/14-abstract-server-pattern.md +476 -476
- package/lib/assets/docs/article/functional-desgin-ppp/scala/15-gossiping-bus-drivers.md +391 -391
- package/lib/assets/docs/article/functional-desgin-ppp/scala/16-payroll-system.md +342 -342
- package/lib/assets/docs/article/functional-desgin-ppp/scala/17-video-rental-system.md +324 -324
- package/lib/assets/docs/article/functional-desgin-ppp/scala/18-concurrency-system.md +730 -730
- package/lib/assets/docs/article/functional-desgin-ppp/scala/19-wator-simulation.md +624 -624
- package/lib/assets/docs/article/functional-desgin-ppp/scala/20-pattern-interactions.md +512 -512
- package/lib/assets/docs/article/functional-desgin-ppp/scala/21-best-practices.md +433 -433
- package/lib/assets/docs/article/functional-desgin-ppp/scala/22-oo-to-fp-migration.md +688 -688
- package/lib/assets/docs/article/functional-desgin-ppp/scala/index.md +243 -243
- package/lib/assets/docs/article/getting-start-tdd/clojure/01-todo-list-and-first-test.md +166 -166
- package/lib/assets/docs/article/getting-start-tdd/clojure/02-fake-it-and-triangulation.md +162 -162
- package/lib/assets/docs/article/getting-start-tdd/clojure/03-obvious-implementation-and-refactoring.md +135 -135
- package/lib/assets/docs/article/getting-start-tdd/clojure/04-version-control-and-conventional-commits.md +88 -88
- package/lib/assets/docs/article/getting-start-tdd/clojure/05-package-management-and-static-analysis.md +299 -299
- package/lib/assets/docs/article/getting-start-tdd/clojure/06-task-runner-and-ci-cd.md +241 -241
- package/lib/assets/docs/article/getting-start-tdd/clojure/07-protocols-and-records.md +131 -131
- package/lib/assets/docs/article/getting-start-tdd/clojure/08-multimethods-and-design-patterns.md +130 -130
- package/lib/assets/docs/article/getting-start-tdd/clojure/09-namespaces-and-module-design.md +127 -127
- package/lib/assets/docs/article/getting-start-tdd/clojure/10-higher-order-functions-and-composition.md +114 -114
- package/lib/assets/docs/article/getting-start-tdd/clojure/11-persistent-data-and-pipeline.md +138 -138
- package/lib/assets/docs/article/getting-start-tdd/clojure/12-error-handling-and-spec.md +161 -161
- package/lib/assets/docs/article/getting-start-tdd/clojure/index.md +65 -65
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter01.md +232 -232
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter02.md +244 -244
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter03.md +202 -202
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter04.md +92 -92
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter05.md +256 -256
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter06.md +195 -195
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter07.md +214 -214
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter08.md +249 -249
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter09.md +174 -174
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter10.md +166 -166
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter11.md +192 -192
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter12.md +211 -211
- package/lib/assets/docs/article/getting-start-tdd/csharp/index.md +83 -83
- package/lib/assets/docs/article/getting-start-tdd/elixir/01-todo-list-and-first-test.md +87 -87
- package/lib/assets/docs/article/getting-start-tdd/elixir/02-fake-it-and-triangulation.md +95 -95
- package/lib/assets/docs/article/getting-start-tdd/elixir/03-obvious-implementation-and-refactoring.md +109 -109
- package/lib/assets/docs/article/getting-start-tdd/elixir/04-version-control-and-conventional-commits.md +96 -96
- package/lib/assets/docs/article/getting-start-tdd/elixir/05-package-management-and-static-analysis.md +88 -88
- package/lib/assets/docs/article/getting-start-tdd/elixir/06-task-runner-and-ci-cd.md +71 -71
- package/lib/assets/docs/article/getting-start-tdd/elixir/07-structs-and-protocols.md +110 -110
- package/lib/assets/docs/article/getting-start-tdd/elixir/08-pattern-matching-and-guards.md +108 -108
- package/lib/assets/docs/article/getting-start-tdd/elixir/09-module-design-and-behaviours.md +104 -104
- package/lib/assets/docs/article/getting-start-tdd/elixir/10-higher-order-functions-and-pipeline.md +178 -178
- package/lib/assets/docs/article/getting-start-tdd/elixir/11-stream-and-lazy-evaluation.md +142 -142
- package/lib/assets/docs/article/getting-start-tdd/elixir/12-error-handling-and-with.md +145 -145
- package/lib/assets/docs/article/getting-start-tdd/elixir/index.md +35 -35
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter01.md +202 -202
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter02.md +246 -246
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter03.md +218 -218
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter04.md +179 -179
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter05.md +267 -267
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter06.md +190 -190
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter07.md +161 -161
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter08.md +175 -175
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter09.md +222 -222
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter10.md +189 -189
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter11.md +212 -212
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter12.md +215 -215
- package/lib/assets/docs/article/getting-start-tdd/fsharp/index.md +71 -71
- package/lib/assets/docs/article/getting-start-tdd/go/01-todo-list-and-first-test.md +213 -213
- package/lib/assets/docs/article/getting-start-tdd/go/02-fake-it-and-triangulation.md +302 -302
- package/lib/assets/docs/article/getting-start-tdd/go/03-obvious-implementation-and-refactoring.md +339 -339
- package/lib/assets/docs/article/getting-start-tdd/go/04-version-control-and-conventional-commits.md +112 -112
- package/lib/assets/docs/article/getting-start-tdd/go/05-package-management-and-static-analysis.md +272 -272
- package/lib/assets/docs/article/getting-start-tdd/go/06-task-runner-and-ci-cd.md +233 -233
- package/lib/assets/docs/article/getting-start-tdd/go/07-encapsulation-and-polymorphism.md +394 -394
- package/lib/assets/docs/article/getting-start-tdd/go/08-design-patterns.md +422 -422
- package/lib/assets/docs/article/getting-start-tdd/go/09-solid-principles-and-module-design.md +400 -400
- package/lib/assets/docs/article/getting-start-tdd/go/10-higher-order-functions-and-composition.md +226 -226
- package/lib/assets/docs/article/getting-start-tdd/go/11-immutable-data-and-pipeline.md +296 -296
- package/lib/assets/docs/article/getting-start-tdd/go/12-error-handling-and-type-safety.md +411 -411
- package/lib/assets/docs/article/getting-start-tdd/go/index.md +83 -83
- package/lib/assets/docs/article/getting-start-tdd/haskell/01-todo-list-and-first-test.md +279 -279
- package/lib/assets/docs/article/getting-start-tdd/haskell/02-fake-it-and-triangulation.md +337 -337
- package/lib/assets/docs/article/getting-start-tdd/haskell/03-obvious-implementation-and-refactoring.md +257 -257
- package/lib/assets/docs/article/getting-start-tdd/haskell/04-version-control-and-conventional-commits.md +182 -182
- package/lib/assets/docs/article/getting-start-tdd/haskell/05-package-management-and-static-analysis.md +313 -313
- package/lib/assets/docs/article/getting-start-tdd/haskell/06-task-runner-and-ci-cd.md +309 -309
- package/lib/assets/docs/article/getting-start-tdd/haskell/07-algebraic-data-types-and-type-classes.md +412 -412
- package/lib/assets/docs/article/getting-start-tdd/haskell/08-pattern-matching-and-guards.md +390 -390
- package/lib/assets/docs/article/getting-start-tdd/haskell/09-module-design-and-smart-constructors.md +461 -461
- package/lib/assets/docs/article/getting-start-tdd/haskell/10-higher-order-functions-and-currying.md +434 -434
- package/lib/assets/docs/article/getting-start-tdd/haskell/11-function-composition-and-point-free.md +392 -392
- package/lib/assets/docs/article/getting-start-tdd/haskell/12-monad-and-error-handling.md +631 -631
- package/lib/assets/docs/article/getting-start-tdd/haskell/index.md +49 -49
- package/lib/assets/docs/article/getting-start-tdd/index.md +93 -93
- package/lib/assets/docs/article/getting-start-tdd/integration/01-language-overview.md +375 -375
- package/lib/assets/docs/article/getting-start-tdd/integration/02-test-framework-comparison.md +349 -349
- package/lib/assets/docs/article/getting-start-tdd/integration/03-tdd-pattern-comparison.md +445 -445
- package/lib/assets/docs/article/getting-start-tdd/integration/04-type-system-comparison.md +409 -409
- package/lib/assets/docs/article/getting-start-tdd/integration/05-dev-environment-comparison.md +330 -330
- package/lib/assets/docs/article/getting-start-tdd/integration/06-learning-roadmap.md +290 -290
- package/lib/assets/docs/article/getting-start-tdd/integration/index.md +69 -69
- package/lib/assets/docs/article/getting-start-tdd/java/01-todo-list-and-first-test.md +234 -234
- package/lib/assets/docs/article/getting-start-tdd/java/02-fake-it-and-triangulation.md +261 -261
- package/lib/assets/docs/article/getting-start-tdd/java/03-obvious-implementation-and-refactoring.md +185 -185
- package/lib/assets/docs/article/getting-start-tdd/java/04-version-control-and-conventional-commits.md +115 -115
- package/lib/assets/docs/article/getting-start-tdd/java/05-package-management-and-static-analysis.md +382 -382
- package/lib/assets/docs/article/getting-start-tdd/java/06-task-runner-and-ci-cd.md +272 -272
- package/lib/assets/docs/article/getting-start-tdd/java/07-encapsulation-and-polymorphism.md +626 -626
- package/lib/assets/docs/article/getting-start-tdd/java/08-design-patterns.md +393 -393
- package/lib/assets/docs/article/getting-start-tdd/java/09-solid-principles-and-module-design.md +310 -310
- package/lib/assets/docs/article/getting-start-tdd/java/10-higher-order-functions-and-composition.md +188 -188
- package/lib/assets/docs/article/getting-start-tdd/java/11-immutable-data-and-pipeline.md +167 -167
- package/lib/assets/docs/article/getting-start-tdd/java/12-error-handling-and-type-safety.md +205 -205
- package/lib/assets/docs/article/getting-start-tdd/java/index.md +61 -61
- package/lib/assets/docs/article/getting-start-tdd/node/01-todo-list-and-first-test.md +244 -244
- package/lib/assets/docs/article/getting-start-tdd/node/02-fake-it-and-triangulation.md +262 -262
- package/lib/assets/docs/article/getting-start-tdd/node/03-obvious-implementation-and-refactoring.md +169 -169
- package/lib/assets/docs/article/getting-start-tdd/node/04-version-control-and-conventional-commits.md +112 -112
- package/lib/assets/docs/article/getting-start-tdd/node/05-package-management-and-static-analysis.md +314 -314
- package/lib/assets/docs/article/getting-start-tdd/node/06-task-runner-and-ci-cd.md +235 -235
- package/lib/assets/docs/article/getting-start-tdd/node/07-encapsulation-and-polymorphism.md +327 -327
- package/lib/assets/docs/article/getting-start-tdd/node/08-design-patterns.md +322 -322
- package/lib/assets/docs/article/getting-start-tdd/node/09-solid-principles-and-module-design.md +285 -285
- package/lib/assets/docs/article/getting-start-tdd/node/10-higher-order-functions-and-composition.md +199 -199
- package/lib/assets/docs/article/getting-start-tdd/node/11-immutable-data-and-pipeline.md +207 -207
- package/lib/assets/docs/article/getting-start-tdd/node/12-error-handling-and-type-safety.md +295 -295
- package/lib/assets/docs/article/getting-start-tdd/node/index.md +56 -56
- package/lib/assets/docs/article/getting-start-tdd/php/01-todo-list-and-first-test.md +259 -259
- package/lib/assets/docs/article/getting-start-tdd/php/02-fake-it-and-triangulation.md +200 -200
- package/lib/assets/docs/article/getting-start-tdd/php/03-obvious-implementation-and-refactoring.md +248 -248
- package/lib/assets/docs/article/getting-start-tdd/php/04-version-control-and-conventional-commits.md +141 -141
- package/lib/assets/docs/article/getting-start-tdd/php/05-package-management-and-static-analysis.md +410 -410
- package/lib/assets/docs/article/getting-start-tdd/php/06-task-runner-and-ci-cd.md +321 -321
- package/lib/assets/docs/article/getting-start-tdd/php/07-encapsulation-and-polymorphism.md +372 -372
- package/lib/assets/docs/article/getting-start-tdd/php/08-design-patterns.md +453 -453
- package/lib/assets/docs/article/getting-start-tdd/php/09-solid-principles-and-module-design.md +460 -460
- package/lib/assets/docs/article/getting-start-tdd/php/10-higher-order-functions-and-composition.md +182 -182
- package/lib/assets/docs/article/getting-start-tdd/php/11-immutable-data-and-pipeline.md +266 -266
- package/lib/assets/docs/article/getting-start-tdd/php/12-error-handling-and-type-safety.md +308 -308
- package/lib/assets/docs/article/getting-start-tdd/php/index.md +84 -84
- package/lib/assets/docs/article/getting-start-tdd/python/01-todo-list-and-first-test.md +201 -201
- package/lib/assets/docs/article/getting-start-tdd/python/02-fake-it-and-triangulation.md +247 -247
- package/lib/assets/docs/article/getting-start-tdd/python/03-obvious-implementation-and-refactoring.md +199 -199
- package/lib/assets/docs/article/getting-start-tdd/python/04-version-control-and-conventional-commits.md +87 -87
- package/lib/assets/docs/article/getting-start-tdd/python/05-package-management-and-static-analysis.md +274 -274
- package/lib/assets/docs/article/getting-start-tdd/python/06-task-runner-and-ci-cd.md +190 -190
- package/lib/assets/docs/article/getting-start-tdd/python/07-encapsulation-and-polymorphism.md +208 -208
- package/lib/assets/docs/article/getting-start-tdd/python/08-design-patterns.md +172 -172
- package/lib/assets/docs/article/getting-start-tdd/python/09-solid-principles-and-module-design.md +130 -130
- package/lib/assets/docs/article/getting-start-tdd/python/10-higher-order-functions-and-composition.md +122 -122
- package/lib/assets/docs/article/getting-start-tdd/python/11-immutable-data-and-pipeline.md +116 -116
- package/lib/assets/docs/article/getting-start-tdd/python/12-error-handling-and-type-safety.md +126 -126
- package/lib/assets/docs/article/getting-start-tdd/python/index.md +55 -55
- package/lib/assets/docs/article/getting-start-tdd/ruby/01-todo-list-and-first-test.md +231 -231
- package/lib/assets/docs/article/getting-start-tdd/ruby/02-fake-it-and-triangulation.md +238 -238
- package/lib/assets/docs/article/getting-start-tdd/ruby/03-obvious-implementation-and-refactoring.md +228 -228
- package/lib/assets/docs/article/getting-start-tdd/ruby/04-version-control-and-conventional-commits.md +112 -112
- package/lib/assets/docs/article/getting-start-tdd/ruby/05-package-management-and-static-analysis.md +287 -287
- package/lib/assets/docs/article/getting-start-tdd/ruby/06-task-runner-and-ci-cd.md +248 -248
- package/lib/assets/docs/article/getting-start-tdd/ruby/07-encapsulation-and-polymorphism.md +279 -279
- package/lib/assets/docs/article/getting-start-tdd/ruby/08-design-patterns.md +329 -329
- package/lib/assets/docs/article/getting-start-tdd/ruby/09-solid-principles-and-module-design.md +196 -196
- package/lib/assets/docs/article/getting-start-tdd/ruby/10-higher-order-functions-and-composition.md +175 -175
- package/lib/assets/docs/article/getting-start-tdd/ruby/11-immutable-data-and-pipeline.md +237 -237
- package/lib/assets/docs/article/getting-start-tdd/ruby/12-error-handling-and-type-safety.md +398 -398
- package/lib/assets/docs/article/getting-start-tdd/ruby/index.md +83 -83
- package/lib/assets/docs/article/getting-start-tdd/rust/01-todo-list-and-first-test.md +211 -211
- package/lib/assets/docs/article/getting-start-tdd/rust/02-fake-it-and-triangulation.md +264 -264
- package/lib/assets/docs/article/getting-start-tdd/rust/03-obvious-implementation-and-refactoring.md +233 -233
- package/lib/assets/docs/article/getting-start-tdd/rust/04-version-control-and-conventional-commits.md +92 -92
- package/lib/assets/docs/article/getting-start-tdd/rust/05-package-management-and-static-analysis.md +212 -212
- package/lib/assets/docs/article/getting-start-tdd/rust/06-task-runner-and-ci-cd.md +164 -164
- package/lib/assets/docs/article/getting-start-tdd/rust/07-encapsulation-and-polymorphism.md +142 -142
- package/lib/assets/docs/article/getting-start-tdd/rust/08-design-patterns.md +145 -145
- package/lib/assets/docs/article/getting-start-tdd/rust/09-solid-principles-and-module-design.md +110 -110
- package/lib/assets/docs/article/getting-start-tdd/rust/10-higher-order-functions-and-composition.md +94 -94
- package/lib/assets/docs/article/getting-start-tdd/rust/11-immutable-data-and-pipeline.md +105 -105
- package/lib/assets/docs/article/getting-start-tdd/rust/12-error-handling-and-type-safety.md +112 -112
- package/lib/assets/docs/article/getting-start-tdd/rust/index.md +83 -83
- package/lib/assets/docs/article/getting-start-tdd/scala/01-todo-list-and-first-test.md +111 -111
- package/lib/assets/docs/article/getting-start-tdd/scala/02-fake-it-and-triangulation.md +107 -107
- package/lib/assets/docs/article/getting-start-tdd/scala/03-obvious-implementation-and-refactoring.md +99 -99
- package/lib/assets/docs/article/getting-start-tdd/scala/04-version-control-and-conventional-commits.md +123 -123
- package/lib/assets/docs/article/getting-start-tdd/scala/05-package-management-and-static-analysis.md +196 -196
- package/lib/assets/docs/article/getting-start-tdd/scala/06-task-runner-and-ci-cd.md +186 -186
- package/lib/assets/docs/article/getting-start-tdd/scala/07-case-classes-and-traits.md +139 -139
- package/lib/assets/docs/article/getting-start-tdd/scala/08-pattern-matching-and-sealed-traits.md +106 -106
- package/lib/assets/docs/article/getting-start-tdd/scala/09-packages-and-module-design.md +75 -75
- package/lib/assets/docs/article/getting-start-tdd/scala/10-higher-order-functions-and-composition.md +104 -104
- package/lib/assets/docs/article/getting-start-tdd/scala/11-collections-and-lazy-evaluation.md +94 -94
- package/lib/assets/docs/article/getting-start-tdd/scala/12-error-handling-and-type-safety.md +92 -92
- package/lib/assets/docs/article/getting-start-tdd/scala/index.md +65 -65
- package/lib/assets/docs/article/grokking-concurrency/all/index.md +404 -404
- package/lib/assets/docs/article/grokking-concurrency/all/part-1-ch02-sequential.md +554 -554
- package/lib/assets/docs/article/grokking-concurrency/all/part-2-ch04-05-threads.md +469 -469
- package/lib/assets/docs/article/grokking-concurrency/all/part-3-ch06-multitasking.md +520 -520
- package/lib/assets/docs/article/grokking-concurrency/all/part-4-ch07-parallel-patterns.md +420 -420
- package/lib/assets/docs/article/grokking-concurrency/all/part-5-ch08-09-synchronization.md +510 -510
- package/lib/assets/docs/article/grokking-concurrency/all/part-6-ch10-11-nonblocking-io.md +435 -435
- package/lib/assets/docs/article/grokking-concurrency/all/part-7-ch12-async.md +465 -465
- package/lib/assets/docs/article/grokking-concurrency/all/part-8-ch13-mapreduce.md +377 -377
- package/lib/assets/docs/article/grokking-concurrency/clojure/index.md +116 -116
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-1.md +108 -108
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-2.md +101 -101
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-3.md +122 -122
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-4.md +123 -123
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-5.md +118 -118
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-6.md +89 -89
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-7.md +100 -100
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-8.md +120 -120
- package/lib/assets/docs/article/grokking-concurrency/csharp/index.md +101 -101
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-1.md +97 -97
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-2.md +123 -123
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-3.md +101 -101
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-4.md +112 -112
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-5.md +99 -99
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-6.md +61 -61
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-7.md +84 -84
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-8.md +92 -92
- package/lib/assets/docs/article/grokking-concurrency/fsharp/index.md +65 -65
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-1.md +80 -80
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-2.md +103 -103
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-3.md +94 -94
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-4.md +110 -110
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-5.md +104 -104
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-6.md +93 -93
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-7.md +121 -121
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-8.md +107 -107
- package/lib/assets/docs/article/grokking-concurrency/haskell/index.md +248 -248
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-1.md +96 -96
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-2.md +96 -96
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-3.md +91 -91
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-4.md +106 -106
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-5.md +99 -99
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-6.md +95 -95
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-7.md +111 -111
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-8.md +118 -118
- package/lib/assets/docs/article/grokking-concurrency/index.md +66 -66
- package/lib/assets/docs/article/grokking-concurrency/java/index.md +102 -102
- package/lib/assets/docs/article/grokking-concurrency/java/part-1.md +308 -308
- package/lib/assets/docs/article/grokking-concurrency/java/part-2.md +334 -334
- package/lib/assets/docs/article/grokking-concurrency/java/part-3.md +221 -221
- package/lib/assets/docs/article/grokking-concurrency/java/part-4.md +213 -213
- package/lib/assets/docs/article/grokking-concurrency/java/part-5.md +112 -112
- package/lib/assets/docs/article/grokking-concurrency/java/part-6.md +69 -69
- package/lib/assets/docs/article/grokking-concurrency/java/part-7.md +101 -101
- package/lib/assets/docs/article/grokking-concurrency/java/part-8.md +101 -101
- package/lib/assets/docs/article/grokking-concurrency/python/index.md +313 -313
- package/lib/assets/docs/article/grokking-concurrency/python/part-1.md +239 -239
- package/lib/assets/docs/article/grokking-concurrency/python/part-2.md +418 -418
- package/lib/assets/docs/article/grokking-concurrency/python/part-3.md +227 -227
- package/lib/assets/docs/article/grokking-concurrency/python/part-4.md +299 -299
- package/lib/assets/docs/article/grokking-concurrency/python/part-5.md +315 -315
- package/lib/assets/docs/article/grokking-concurrency/python/part-6.md +297 -297
- package/lib/assets/docs/article/grokking-concurrency/python/part-7.md +314 -314
- package/lib/assets/docs/article/grokking-concurrency/python/part-8.md +360 -360
- package/lib/assets/docs/article/grokking-concurrency/rust/index.md +270 -270
- package/lib/assets/docs/article/grokking-concurrency/rust/part-1.md +108 -108
- package/lib/assets/docs/article/grokking-concurrency/rust/part-2.md +120 -120
- package/lib/assets/docs/article/grokking-concurrency/rust/part-3.md +126 -126
- package/lib/assets/docs/article/grokking-concurrency/rust/part-4.md +175 -175
- package/lib/assets/docs/article/grokking-concurrency/rust/part-5.md +158 -158
- package/lib/assets/docs/article/grokking-concurrency/rust/part-6.md +94 -94
- package/lib/assets/docs/article/grokking-concurrency/rust/part-7.md +133 -133
- package/lib/assets/docs/article/grokking-concurrency/rust/part-8.md +155 -155
- package/lib/assets/docs/article/grokking-concurrency/scala/index.md +69 -69
- package/lib/assets/docs/article/grokking-concurrency/scala/part-1.md +78 -78
- package/lib/assets/docs/article/grokking-concurrency/scala/part-2.md +112 -112
- package/lib/assets/docs/article/grokking-concurrency/scala/part-3.md +93 -93
- package/lib/assets/docs/article/grokking-concurrency/scala/part-4.md +110 -110
- package/lib/assets/docs/article/grokking-concurrency/scala/part-5.md +119 -119
- package/lib/assets/docs/article/grokking-concurrency/scala/part-6.md +83 -83
- package/lib/assets/docs/article/grokking-concurrency/scala/part-7.md +131 -131
- package/lib/assets/docs/article/grokking-concurrency/scala/part-8.md +129 -129
- package/lib/assets/docs/article/grokkingfp/all/index.md +368 -368
- package/lib/assets/docs/article/grokkingfp/all/part-1-ch01-fp-introduction.md +530 -530
- package/lib/assets/docs/article/grokkingfp/all/part-1-ch02-pure-functions.md +923 -923
- package/lib/assets/docs/article/grokkingfp/all/part-2-ch03-immutable-data.md +1128 -1128
- package/lib/assets/docs/article/grokkingfp/all/part-2-ch04-higher-order-functions.md +1104 -1104
- package/lib/assets/docs/article/grokkingfp/all/part-2-ch05-flatmap.md +1026 -1026
- package/lib/assets/docs/article/grokkingfp/all/part-3-ch06-option.md +785 -785
- package/lib/assets/docs/article/grokkingfp/all/part-3-ch07-either-adt.md +871 -871
- package/lib/assets/docs/article/grokkingfp/all/part-4-ch08-io-monad.md +972 -972
- package/lib/assets/docs/article/grokkingfp/all/part-4-ch09-streams.md +926 -926
- package/lib/assets/docs/article/grokkingfp/all/part-5-ch10-concurrency.md +870 -870
- package/lib/assets/docs/article/grokkingfp/all/part-6-ch11-application.md +715 -715
- package/lib/assets/docs/article/grokkingfp/all/part-6-ch12-testing.md +626 -626
- package/lib/assets/docs/article/grokkingfp/all/writing-plan.md +712 -712
- package/lib/assets/docs/article/grokkingfp/clojure/index.md +276 -276
- package/lib/assets/docs/article/grokkingfp/clojure/part-1.md +667 -667
- package/lib/assets/docs/article/grokkingfp/clojure/part-2.md +643 -643
- package/lib/assets/docs/article/grokkingfp/clojure/part-3.md +620 -620
- package/lib/assets/docs/article/grokkingfp/clojure/part-4.md +697 -697
- package/lib/assets/docs/article/grokkingfp/clojure/part-5.md +751 -751
- package/lib/assets/docs/article/grokkingfp/clojure/part-6.md +721 -721
- package/lib/assets/docs/article/grokkingfp/csharp/index.md +246 -246
- package/lib/assets/docs/article/grokkingfp/csharp/part-1.md +811 -811
- package/lib/assets/docs/article/grokkingfp/csharp/part-2.md +971 -971
- package/lib/assets/docs/article/grokkingfp/csharp/part-3.md +981 -981
- package/lib/assets/docs/article/grokkingfp/csharp/part-4.md +949 -949
- package/lib/assets/docs/article/grokkingfp/csharp/part-5.md +947 -947
- package/lib/assets/docs/article/grokkingfp/csharp/part-6.md +739 -739
- package/lib/assets/docs/article/grokkingfp/elixir/index.md +203 -203
- package/lib/assets/docs/article/grokkingfp/elixir/part-1.md +712 -712
- package/lib/assets/docs/article/grokkingfp/elixir/part-2.md +838 -838
- package/lib/assets/docs/article/grokkingfp/elixir/part-3.md +985 -985
- package/lib/assets/docs/article/grokkingfp/elixir/part-4.md +974 -974
- package/lib/assets/docs/article/grokkingfp/elixir/part-5.md +1286 -1286
- package/lib/assets/docs/article/grokkingfp/elixir/part-6.md +1049 -1049
- package/lib/assets/docs/article/grokkingfp/fsharp/index.md +210 -210
- package/lib/assets/docs/article/grokkingfp/fsharp/part-1.md +714 -714
- package/lib/assets/docs/article/grokkingfp/fsharp/part-2.md +961 -961
- package/lib/assets/docs/article/grokkingfp/fsharp/part-3.md +972 -972
- package/lib/assets/docs/article/grokkingfp/fsharp/part-4.md +832 -832
- package/lib/assets/docs/article/grokkingfp/fsharp/part-5.md +911 -911
- package/lib/assets/docs/article/grokkingfp/fsharp/part-6.md +922 -922
- package/lib/assets/docs/article/grokkingfp/haskell/index.md +234 -234
- package/lib/assets/docs/article/grokkingfp/haskell/part-1.md +591 -591
- package/lib/assets/docs/article/grokkingfp/haskell/part-2.md +866 -866
- package/lib/assets/docs/article/grokkingfp/haskell/part-3.md +915 -915
- package/lib/assets/docs/article/grokkingfp/haskell/part-4.md +878 -878
- package/lib/assets/docs/article/grokkingfp/haskell/part-5.md +845 -845
- package/lib/assets/docs/article/grokkingfp/haskell/part-6.md +844 -844
- package/lib/assets/docs/article/grokkingfp/index.md +143 -143
- package/lib/assets/docs/article/grokkingfp/java/index.md +211 -211
- package/lib/assets/docs/article/grokkingfp/java/part-1.md +648 -648
- package/lib/assets/docs/article/grokkingfp/java/part-2.md +675 -675
- package/lib/assets/docs/article/grokkingfp/java/part-3.md +672 -672
- package/lib/assets/docs/article/grokkingfp/java/part-4.md +771 -771
- package/lib/assets/docs/article/grokkingfp/java/part-5.md +959 -959
- package/lib/assets/docs/article/grokkingfp/java/part-6.md +1328 -1328
- package/lib/assets/docs/article/grokkingfp/python/index.md +258 -258
- package/lib/assets/docs/article/grokkingfp/python/part-1.md +443 -443
- package/lib/assets/docs/article/grokkingfp/python/part-2.md +958 -958
- package/lib/assets/docs/article/grokkingfp/python/part-3.md +1004 -1004
- package/lib/assets/docs/article/grokkingfp/python/part-4.md +765 -765
- package/lib/assets/docs/article/grokkingfp/python/part-5.md +747 -747
- package/lib/assets/docs/article/grokkingfp/python/part-6.md +861 -861
- package/lib/assets/docs/article/grokkingfp/ruby/index.md +330 -330
- package/lib/assets/docs/article/grokkingfp/ruby/part-1.md +755 -755
- package/lib/assets/docs/article/grokkingfp/ruby/part-2.md +938 -938
- package/lib/assets/docs/article/grokkingfp/ruby/part-3.md +946 -946
- package/lib/assets/docs/article/grokkingfp/ruby/part-4.md +921 -921
- package/lib/assets/docs/article/grokkingfp/ruby/part-5.md +908 -908
- package/lib/assets/docs/article/grokkingfp/ruby/part-6.md +1412 -1412
- package/lib/assets/docs/article/grokkingfp/rust/index.md +242 -242
- package/lib/assets/docs/article/grokkingfp/rust/part-1.md +634 -634
- package/lib/assets/docs/article/grokkingfp/rust/part-2.md +1060 -1060
- package/lib/assets/docs/article/grokkingfp/rust/part-3.md +994 -994
- package/lib/assets/docs/article/grokkingfp/rust/part-4.md +573 -573
- package/lib/assets/docs/article/grokkingfp/rust/part-5.md +705 -705
- package/lib/assets/docs/article/grokkingfp/rust/part-6.md +508 -508
- package/lib/assets/docs/article/grokkingfp/scala/index.md +171 -171
- package/lib/assets/docs/article/grokkingfp/scala/part-1.md +543 -543
- package/lib/assets/docs/article/grokkingfp/scala/part-2.md +946 -946
- package/lib/assets/docs/article/grokkingfp/scala/part-3.md +919 -919
- package/lib/assets/docs/article/grokkingfp/scala/part-4.md +742 -742
- package/lib/assets/docs/article/grokkingfp/scala/part-5.md +722 -722
- package/lib/assets/docs/article/grokkingfp/scala/part-6.md +867 -867
- package/lib/assets/docs/article/grokkingfp/typescript/index.md +273 -273
- package/lib/assets/docs/article/grokkingfp/typescript/part-1.md +561 -561
- package/lib/assets/docs/article/grokkingfp/typescript/part-2.md +1129 -1129
- package/lib/assets/docs/article/grokkingfp/typescript/part-3.md +842 -842
- package/lib/assets/docs/article/grokkingfp/typescript/part-4.md +1087 -1087
- package/lib/assets/docs/article/grokkingfp/typescript/part-5.md +717 -717
- package/lib/assets/docs/article/grokkingfp/typescript/part-6.md +982 -982
- package/lib/assets/docs/article/practical-database-design/index.md +121 -121
- package/lib/assets/docs/article/practical-database-design/part1/chapter01.md +288 -288
- package/lib/assets/docs/article/practical-database-design/part1/chapter02.md +518 -518
- package/lib/assets/docs/article/practical-database-design/part1/chapter03.md +557 -557
- package/lib/assets/docs/article/practical-database-design/part2/chapter04.md +924 -924
- package/lib/assets/docs/article/practical-database-design/part2/chapter05.md +1627 -1627
- package/lib/assets/docs/article/practical-database-design/part2/chapter06.md +2716 -2716
- package/lib/assets/docs/article/practical-database-design/part2/chapter07.md +2082 -2082
- package/lib/assets/docs/article/practical-database-design/part2/chapter08.md +2105 -2105
- package/lib/assets/docs/article/practical-database-design/part2/chapter09.md +2031 -2031
- package/lib/assets/docs/article/practical-database-design/part2/chapter10.md +1387 -1387
- package/lib/assets/docs/article/practical-database-design/part2/chapter11.md +1677 -1677
- package/lib/assets/docs/article/practical-database-design/part2/chapter12.md +1417 -1417
- package/lib/assets/docs/article/practical-database-design/part2/chapter13.md +1434 -1434
- package/lib/assets/docs/article/practical-database-design/part3/chapter14.md +667 -667
- package/lib/assets/docs/article/practical-database-design/part3/chapter15.md +1625 -1625
- package/lib/assets/docs/article/practical-database-design/part3/chapter16.md +1915 -1915
- package/lib/assets/docs/article/practical-database-design/part3/chapter17.md +1708 -1708
- package/lib/assets/docs/article/practical-database-design/part3/chapter18.md +2095 -2095
- package/lib/assets/docs/article/practical-database-design/part3/chapter19.md +1123 -1123
- package/lib/assets/docs/article/practical-database-design/part3/chapter20.md +1031 -1031
- package/lib/assets/docs/article/practical-database-design/part3/chapter21.md +1382 -1382
- package/lib/assets/docs/article/practical-database-design/part3-orm/chapter14-orm.md +991 -991
- package/lib/assets/docs/article/practical-database-design/part3-orm/chapter15-orm.md +1300 -1300
- package/lib/assets/docs/article/practical-database-design/part3-orm/chapter16-orm.md +1166 -1166
- package/lib/assets/docs/article/practical-database-design/part3-orm/chapter17-orm.md +1584 -1584
- package/lib/assets/docs/article/practical-database-design/part3-orm/chapter18-orm.md +1183 -1183
- package/lib/assets/docs/article/practical-database-design/part3-orm/chapter19-orm.md +1016 -1016
- package/lib/assets/docs/article/practical-database-design/part3-orm/chapter20-orm.md +1753 -1753
- package/lib/assets/docs/article/practical-database-design/part3-orm/chapter21-orm.md +1447 -1447
- package/lib/assets/docs/article/practical-database-design/part3-orm/chapter22-orm.md +1878 -1878
- package/lib/assets/docs/article/practical-database-design/part4/chapter22.md +965 -965
- package/lib/assets/docs/article/practical-database-design/part4/chapter23.md +2069 -2069
- package/lib/assets/docs/article/practical-database-design/part4/chapter24.md +2439 -2439
- package/lib/assets/docs/article/practical-database-design/part4/chapter25.md +3661 -3661
- package/lib/assets/docs/article/practical-database-design/part4/chapter26.md +2916 -2916
- package/lib/assets/docs/article/practical-database-design/part4/chapter27.md +3105 -3105
- package/lib/assets/docs/article/practical-database-design/part4/chapter28.md +2697 -2697
- package/lib/assets/docs/article/practical-database-design/part4/chapter29.md +2544 -2544
- package/lib/assets/docs/article/practical-database-design/part4/chapter30.md +2180 -2180
- package/lib/assets/docs/article/practical-database-design/part4/chapter31.md +1192 -1192
- package/lib/assets/docs/article/practical-database-design/part4/chapter32.md +2101 -2101
- package/lib/assets/docs/article/practical-database-design/part5/chapter33.md +1032 -1032
- package/lib/assets/docs/article/practical-database-design/part5/chapter34.md +1609 -1609
- package/lib/assets/docs/article/practical-database-design/part5/chapter35.md +1453 -1453
- package/lib/assets/docs/article/practical-database-design/part5/chapter36.md +1292 -1292
- package/lib/assets/docs/article/practical-database-design/part5/chapter37.md +1470 -1470
- package/lib/assets/docs/article/practical-database-design/part5/chapter38.md +1698 -1698
- package/lib/assets/docs/article/practical-database-design/part5/chapter39.md +2334 -2334
- package/lib/assets/docs/article/practical-database-design/study/study2-1.md +1693 -1693
- package/lib/assets/docs/article/practical-database-design/study/study2-2.md +1347 -1347
- package/lib/assets/docs/article/practical-database-design/study/study2-3.md +2044 -2044
- package/lib/assets/docs/article/practical-database-design/study/study2-4.md +2229 -2229
- package/lib/assets/docs/article/practical-database-design/study/study2-5.md +2418 -2418
- package/lib/assets/docs/article/practical-database-design/study/study3-1.md +2205 -2205
- package/lib/assets/docs/article/practical-database-design/study/study3-2.md +2221 -2221
- package/lib/assets/docs/article/practical-database-design/study/study3-3.md +2253 -2253
- package/lib/assets/docs/article/practical-database-design/study/study3-4.md +2106 -2106
- package/lib/assets/docs/article/practical-database-design/study/study3-5.md +2507 -2507
- package/lib/assets/docs/article/practical-database-design/study/study4-1.md +2587 -2587
- package/lib/assets/docs/article/practical-database-design/study/study4-2.md +2075 -2075
- package/lib/assets/docs/article/practical-database-design/study/study4-3.md +1805 -1805
- package/lib/assets/docs/article/practical-database-design/study/study4-4.md +1895 -1895
- package/lib/assets/docs/article/practical-database-design/study/study4-5.md +2878 -2878
- package/lib/assets/docs/assets/css/extra.css +29 -29
- package/lib/assets/docs/assets/js/extra.js +44 -44
- package/lib/assets/docs/development/index.md +39 -39
- package/lib/assets/docs/operation/index.md +11 -11
- package/lib/assets/docs/reference/CodexCLIMCP/343/202/242/343/203/227/343/203/252/343/202/261/343/203/274/343/202/267/343/203/247/343/203/263/351/226/213/347/231/272/343/203/225/343/203/255/343/203/274.md +532 -532
- package/lib/assets/docs/reference/CodexCLIMCP/343/202/265/343/203/274/343/203/220/343/203/274/350/250/255/345/256/232/346/211/213/351/240/206.md +341 -341
- package/lib/assets/docs/reference/Java/343/202/242/343/203/227/343/203/252/343/202/261/343/203/274/343/202/267/343/203/247/343/203/263/347/222/260/345/242/203/346/247/213/347/257/211/343/202/254/343/202/244/343/203/211.md +581 -580
- package/lib/assets/docs/reference/SonarQube/343/203/255/343/203/274/343/202/253/343/203/253/347/222/260/345/242/203/343/202/273/343/203/203/343/203/210/343/202/242/343/203/203/343/203/227/346/211/213/351/240/206/346/233/270.md +642 -642
- package/lib/assets/docs/reference/TypeScript/343/202/242/343/203/227/343/203/252/343/202/261/343/203/274/343/202/267/343/203/247/343/203/263/347/222/260/345/242/203/346/247/213/347/257/211/343/202/254/343/202/244/343/203/211.md +465 -465
- package/lib/assets/docs/reference/UI/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +450 -450
- package/lib/assets/docs/reference/images/Ansoff.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/BrandBasicStrategy.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/BrandCategorization.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/BrandRecurutementStrategy.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/BrandValue.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/BusinessActivitiy.svg +3 -3
- package/lib/assets/docs/reference/images/HRM.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/MarketingStructure.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/OrganizationElemnts.svg +3 -3
- package/lib/assets/docs/reference/images/PPM.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/PositioningMap.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/ProductLayer.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/ProductMix.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/SWOT.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/TargetMarket.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/ThreeGenericStrategies.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/VRIO.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/ValueChain.drawio.svg +3 -3
- package/lib/assets/docs/reference/index.md +52 -52
- package/lib/assets/docs/reference//343/202/210/343/201/204/343/202/275/343/203/225/343/203/210/343/202/246/343/202/247/343/202/242/343/201/250/343/201/257.md +250 -242
- package/lib/assets/docs/reference//343/202/242/343/203/274/343/202/255/343/203/206/343/202/257/343/203/201/343/203/243/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +2216 -2216
- package/lib/assets/docs/reference//343/202/244/343/203/263/343/203/225/343/203/251/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +1878 -1878
- package/lib/assets/docs/reference//343/202/250/343/202/257/343/202/271/343/203/210/343/203/252/343/203/274/343/203/240/343/203/227/343/203/255/343/202/260/343/203/251/343/203/237/343/203/263/343/202/260.md +550 -544
- package/lib/assets/docs/reference//343/202/263/343/203/274/343/203/207/343/202/243/343/203/263/343/202/260/343/201/250/343/203/206/343/202/271/343/203/210/343/202/254/343/202/244/343/203/211.md +705 -705
- package/lib/assets/docs/reference//343/203/206/343/202/271/343/203/210/346/210/246/347/225/245/343/202/254/343/202/244/343/203/211.md +1313 -1313
- package/lib/assets/docs/reference//343/203/207/343/203/274/343/202/277/343/203/242/343/203/207/343/203/253/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +311 -311
- package/lib/assets/docs/reference//343/203/211/343/203/241/343/202/244/343/203/263/343/203/242/343/203/207/343/203/253/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +599 -599
- package/lib/assets/docs/reference//343/203/223/343/202/270/343/203/215/343/202/271/343/202/242/343/203/274/343/202/255/343/203/206/343/202/257/343/203/201/343/203/243/345/210/206/346/236/220/343/202/254/343/202/244/343/203/211.md +528 -528
- package/lib/assets/docs/reference//343/203/246/343/203/274/343/202/271/343/202/261/343/203/274/343/202/271/344/275/234/346/210/220/343/202/254/343/202/244/343/203/211.md +689 -682
- package/lib/assets/docs/reference//343/203/252/343/203/252/343/203/274/343/202/271/343/202/254/343/202/244/343/203/211.md +461 -461
- package/lib/assets/docs/reference//343/203/252/343/203/252/343/203/274/343/202/271/343/203/273/343/202/244/343/203/206/343/203/254/343/203/274/343/202/267/343/203/247/343/203/263/350/250/210/347/224/273/343/202/254/343/202/244/343/203/211.md +580 -560
- package/lib/assets/docs/reference//343/203/255/343/202/270/343/202/253/343/203/253/343/202/267/343/203/263/343/202/255/343/203/263/343/202/260.md +1367 -1367
- package/lib/assets/docs/reference//344/274/201/346/245/255/347/265/214/345/226/266/350/253/226.md +2637 -2636
- package/lib/assets/docs/reference//347/222/260/345/242/203/345/244/211/346/225/260/347/256/241/347/220/206/343/202/254/343/202/244/343/203/211.md +665 -663
- package/lib/assets/docs/reference//350/246/201/344/273/266/345/256/232/347/276/251/343/202/254/343/202/244/343/203/211.md +1248 -1248
- package/lib/assets/docs/reference//350/250/200/350/252/236/345/210/245/351/226/213/347/231/272/343/202/254/343/202/244/343/203/211.md +28 -0
- package/lib/assets/docs/reference//351/201/213/345/226/266/347/256/241/347/220/206.md +1482 -1482
- package/lib/assets/docs/reference//351/201/213/347/224/250/343/202/271/343/202/257/343/203/252/343/203/227/343/203/210/344/275/234/346/210/220/343/202/254/343/202/244/343/203/211.md +421 -421
- package/lib/assets/docs/reference//351/201/213/347/224/250/350/246/201/344/273/266/345/256/232/347/276/251/343/202/254/343/202/244/343/203/211.md +392 -392
- package/lib/assets/docs/reference//351/226/213/347/231/272/343/202/254/343/202/244/343/203/211.md +299 -299
- package/lib/assets/docs/reference//351/235/236/346/251/237/350/203/275/350/246/201/344/273/266/345/256/232/347/276/251/343/202/254/343/202/244/343/203/211.md +1236 -1236
- package/lib/assets/docs/review/index.md +5 -5
- package/lib/assets/docs/strategy/index.md +1 -1
- package/lib/assets/docs/template/ADR.md +30 -30
- package/lib/assets/docs/template/AWS/343/202/271/343/203/206/343/203/274/343/202/270/343/203/263/343/202/260/347/222/260/345/242/203/343/202/273/343/203/203/343/203/210/343/202/242/343/203/203/343/203/227/346/211/213/351/240/206/346/233/270.md +1366 -1366
- package/lib/assets/docs/template/AWS/343/203/227/343/203/255/343/203/200/343/202/257/343/202/267/343/203/247/343/203/263/347/222/260/345/242/203/343/202/273/343/203/203/343/203/210/343/202/242/343/203/203/343/203/227/346/211/213/351/240/206/346/233/270.md +634 -634
- package/lib/assets/docs/template/README.md +50 -50
- package/lib/assets/docs/template/index.md +23 -23
- package/lib/assets/docs/template//343/201/276/343/201/232/343/201/223/343/202/214/343/202/222/350/252/255/343/202/202/343/201/206/343/203/252/343/202/271/343/203/210.md +12 -12
- package/lib/assets/docs/template//343/202/242/343/203/227/343/203/252/343/202/261/343/203/274/343/202/267/343/203/247/343/203/263/351/226/213/347/231/272/347/222/260/345/242/203/343/202/273/343/203/203/343/203/210/343/202/242/343/203/203/343/203/227/346/211/213/351/240/206/346/233/270.md +547 -547
- package/lib/assets/docs/template//343/202/244/343/203/206/343/203/254/343/203/274/343/202/267/343/203/247/343/203/263/345/256/214/344/272/206/345/240/261/345/221/212/346/233/270.md +58 -58
- package/lib/assets/docs/template//343/202/244/343/203/263/343/202/273/343/203/227/343/202/267/343/203/247/343/203/263/343/203/207/343/203/203/343/202/255.md +13 -13
- package/lib/assets/docs/template//343/203/223/343/202/270/343/203/215/343/202/271/343/202/242/343/203/274/343/202/255/343/203/206/343/202/257/343/203/201/343/203/243.md +379 -379
- package/lib/assets/docs/template//344/274/201/346/245/255/345/210/206/346/236/220.md +573 -573
- package/lib/assets/docs/template//345/256/214/345/205/250/345/275/242/345/274/217/343/201/256/343/203/246/343/203/274/343/202/271/343/202/261/343/203/274/343/202/271.md +69 -68
- package/lib/assets/docs/template//350/246/201/344/273/266/345/256/232/347/276/251.md +669 -669
- package/lib/assets/docs/template//350/250/255/350/250/210.md +173 -173
- package/lib/assets/docs/template//351/226/213/347/231/272/347/222/260/345/242/203/343/202/273/343/203/203/343/203/210/343/202/242/343/203/203/343/203/227/346/211/213/351/240/206/346/233/270.md +688 -688
- package/lib/assets/gulpfile.js +25 -25
- package/lib/assets/mkdocs.yml +136 -135
- package/lib/assets/ops/docker/mkdoc/Dockerfile +19 -19
- package/lib/assets/ops/scripts/journal.js +180 -180
- package/lib/assets/ops/scripts/mkdocs.js +82 -82
- package/lib/assets/ops/scripts/release.js +431 -431
- package/lib/assets/ops/scripts/sonar_local.js +726 -726
- package/lib/assets/ops/scripts/ssh.js +190 -190
- package/lib/assets/ops/scripts/vault.js +299 -299
- package/lib/assets/package-lock.json +1653 -1653
- package/lib/assets/package.json +40 -40
- package/lib/gulpfile.js +37 -37
- package/package.json +41 -41
- package/lib/assets/.claude/agent-memory/xp-programmer/MEMORY.md +0 -6
- package/lib/assets/.claude/agent-memory/xp-programmer/project_cargo_tracker.md +0 -11
- package/lib/assets/.claude/agent-memory/xp-programmer/project_ddd_patterns.md +0 -27
- package/lib/assets/.claude/agent-memory/xp-programmer/project_us07_route_assignment.md +0 -19
|
@@ -1,1347 +1,1347 @@
|
|
|
1
|
-
# 実践データベース設計:販売管理システム 研究 2 - JavaFX デスクトップアプリケーションの実装
|
|
2
|
-
|
|
3
|
-
## はじめに
|
|
4
|
-
|
|
5
|
-
本研究では、API サーバー構成(第13章)やモノリスサービス(研究 1)とは異なるアプローチとして、**JavaFX** によるデスクトップアプリケーションを実装します。FXML による宣言的 UI 定義と、データバインディングによるリアクティブな画面更新を活用した、業務アプリケーションを構築します。
|
|
6
|
-
|
|
7
|
-
研究 1 で構築したヘキサゴナルアーキテクチャ(ドメイン層・アプリケーション層)はそのまま共有し、**Input Adapter として JavaFX の Controller/ViewModel 層のみを追加**します。
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## 第14章:JavaFX デスクトップアプリケーションの基礎
|
|
12
|
-
|
|
13
|
-
### 14.1 JavaFX とは
|
|
14
|
-
|
|
15
|
-
JavaFX は Java のリッチクライアントプラットフォームです。Swing の後継として位置づけられ、モダンな UI 構築が可能です。
|
|
16
|
-
|
|
17
|
-
```plantuml
|
|
18
|
-
@startuml javafx_architecture
|
|
19
|
-
!define RECTANGLE class
|
|
20
|
-
|
|
21
|
-
skinparam backgroundColor #FEFEFE
|
|
22
|
-
|
|
23
|
-
package "JavaFX Architecture (販売管理システム)" {
|
|
24
|
-
|
|
25
|
-
package "View Layer" {
|
|
26
|
-
RECTANGLE "FXML Files\n(宣言的 UI 定義)" as fxml {
|
|
27
|
-
- MainView.fxml
|
|
28
|
-
- ProductView.fxml
|
|
29
|
-
- OrderView.fxml
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
RECTANGLE "CSS Stylesheets\n(スタイル定義)" as css {
|
|
33
|
-
- application.css
|
|
34
|
-
- theme-light.css
|
|
35
|
-
- theme-dark.css
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
package "Controller/ViewModel Layer" {
|
|
40
|
-
RECTANGLE "Controllers\n(画面制御)" as controller {
|
|
41
|
-
- MainController
|
|
42
|
-
- ProductController
|
|
43
|
-
- OrderController
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
RECTANGLE "ViewModels\n(画面状態管理)" as viewmodel {
|
|
47
|
-
- ProductViewModel
|
|
48
|
-
- OrderViewModel
|
|
49
|
-
- CustomerViewModel
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
package "Application Core" {
|
|
54
|
-
RECTANGLE "Use Cases\n(ビジネスロジック)" as usecase {
|
|
55
|
-
- ProductUseCase
|
|
56
|
-
- OrderUseCase
|
|
57
|
-
- InvoiceUseCase
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
fxml --> controller : "fx:controller"
|
|
63
|
-
css --> fxml : "stylesheets"
|
|
64
|
-
controller --> viewmodel : "データバインド"
|
|
65
|
-
viewmodel --> usecase : "Input Port"
|
|
66
|
-
|
|
67
|
-
note bottom of fxml
|
|
68
|
-
Scene Builder で
|
|
69
|
-
視覚的に編集可能
|
|
70
|
-
end note
|
|
71
|
-
|
|
72
|
-
note right of viewmodel
|
|
73
|
-
ObservableList,
|
|
74
|
-
Property による
|
|
75
|
-
双方向バインディング
|
|
76
|
-
end note
|
|
77
|
-
|
|
78
|
-
@enduml
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
**Web アプリケーションとの比較:**
|
|
82
|
-
|
|
83
|
-
| 特徴 | Web アプリ(REST API) | JavaFX デスクトップ |
|
|
84
|
-
|------|----------------------|-------------------|
|
|
85
|
-
| 配置 | サーバー + ブラウザ | ローカルインストール |
|
|
86
|
-
| オフライン | 不可 | 可能(ローカル DB) |
|
|
87
|
-
| レスポンス | ネットワーク依存 | 高速 |
|
|
88
|
-
| UI 自由度 | HTML/CSS 制約 | ネイティブ制御 |
|
|
89
|
-
| 印刷機能 | 制限あり | 高機能 |
|
|
90
|
-
| 更新配布 | 自動 | 手動/自動更新 |
|
|
91
|
-
|
|
92
|
-
---
|
|
93
|
-
|
|
94
|
-
### 14.2 MVVM パターンの採用
|
|
95
|
-
|
|
96
|
-
JavaFX では、**MVVM(Model-View-ViewModel)** パターンを採用し、UI とビジネスロジックを分離します。
|
|
97
|
-
|
|
98
|
-
```plantuml
|
|
99
|
-
@startuml mvvm_pattern
|
|
100
|
-
!define RECTANGLE class
|
|
101
|
-
|
|
102
|
-
skinparam backgroundColor #FEFEFE
|
|
103
|
-
|
|
104
|
-
package "MVVM Pattern" {
|
|
105
|
-
|
|
106
|
-
RECTANGLE "View\n(FXML + Controller)" as view {
|
|
107
|
-
+ TableView<ProductRow>
|
|
108
|
-
+ TextField
|
|
109
|
-
+ Button
|
|
110
|
-
--
|
|
111
|
-
initialize()
|
|
112
|
-
onSaveClick()
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
RECTANGLE "ViewModel\n(画面状態)" as vm {
|
|
116
|
-
+ ObservableList<ProductRow> products
|
|
117
|
-
+ StringProperty searchKeyword
|
|
118
|
-
+ BooleanProperty loading
|
|
119
|
-
--
|
|
120
|
-
search()
|
|
121
|
-
save()
|
|
122
|
-
delete()
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
RECTANGLE "Model\n(Domain + UseCase)" as model {
|
|
126
|
-
+ Product
|
|
127
|
-
+ ProductUseCase
|
|
128
|
-
+ ProductRepository
|
|
129
|
-
--
|
|
130
|
-
findByCode()
|
|
131
|
-
save()
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
view --> vm : "Data Binding\n(双方向)"
|
|
136
|
-
vm --> model : "UseCase 呼び出し"
|
|
137
|
-
model --> vm : "結果を Property に反映"
|
|
138
|
-
vm --> view : "自動 UI 更新"
|
|
139
|
-
|
|
140
|
-
note right of view
|
|
141
|
-
FXML で宣言的に定義
|
|
142
|
-
Controller で
|
|
143
|
-
イベントハンドリング
|
|
144
|
-
end note
|
|
145
|
-
|
|
146
|
-
note right of vm
|
|
147
|
-
JavaFX Property で
|
|
148
|
-
リアクティブな
|
|
149
|
-
状態管理
|
|
150
|
-
end note
|
|
151
|
-
|
|
152
|
-
note right of model
|
|
153
|
-
API版と共有
|
|
154
|
-
変更不要
|
|
155
|
-
end note
|
|
156
|
-
|
|
157
|
-
@enduml
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
**各層の責務:**
|
|
161
|
-
|
|
162
|
-
| 層 | 責務 | JavaFX での実装 |
|
|
163
|
-
|----|------|----------------|
|
|
164
|
-
| **View** | UI 表示・ユーザー入力 | FXML + Controller |
|
|
165
|
-
| **ViewModel** | 画面状態管理・変換 | Property, ObservableList |
|
|
166
|
-
| **Model** | ビジネスロジック | UseCase, Repository |
|
|
167
|
-
|
|
168
|
-
---
|
|
169
|
-
|
|
170
|
-
### 14.3 ヘキサゴナルアーキテクチャとの統合
|
|
171
|
-
|
|
172
|
-
JavaFX を導入しても、既存のヘキサゴナルアーキテクチャ(ドメイン層・アプリケーション層)はそのまま共有し、**Input Adapter として JavaFX の Controller/ViewModel 層のみを追加**します。
|
|
173
|
-
|
|
174
|
-
```plantuml
|
|
175
|
-
@startuml hexagonal_javafx
|
|
176
|
-
!define RECTANGLE class
|
|
177
|
-
|
|
178
|
-
package "Hexagonal Architecture (JavaFX版)" {
|
|
179
|
-
|
|
180
|
-
RECTANGLE "Application Core\n(Domain + Use Cases)" as core {
|
|
181
|
-
- Product (商品)
|
|
182
|
-
- Partner (取引先)
|
|
183
|
-
- Order (受注)
|
|
184
|
-
- Shipment (出荷)
|
|
185
|
-
- ProductUseCase
|
|
186
|
-
- OrderUseCase
|
|
187
|
-
- ShipmentUseCase
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
RECTANGLE "Input Adapters\n(Driving Side)" as input {
|
|
191
|
-
- REST Controller(API版)
|
|
192
|
-
- JavaFX Controller(新規追加)
|
|
193
|
-
- ViewModel
|
|
194
|
-
- FXML View
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
RECTANGLE "Output Adapters\n(Driven Side)" as output {
|
|
198
|
-
- MyBatis Repository
|
|
199
|
-
- Database Access
|
|
200
|
-
- Entity Mapping
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
input --> core : "Input Ports\n(Use Cases)"
|
|
205
|
-
core --> output : "Output Ports\n(Repository Interfaces)"
|
|
206
|
-
|
|
207
|
-
note top of core
|
|
208
|
-
既存のビジネスロジック
|
|
209
|
-
REST API 版と完全に共有
|
|
210
|
-
JavaFX 固有のコードは含まない
|
|
211
|
-
end note
|
|
212
|
-
|
|
213
|
-
note left of input
|
|
214
|
-
JavaFX Controller を
|
|
215
|
-
Input Adapter として追加
|
|
216
|
-
既存の REST API と共存可能
|
|
217
|
-
end note
|
|
218
|
-
|
|
219
|
-
note right of output
|
|
220
|
-
既存の Repository を
|
|
221
|
-
そのまま使用
|
|
222
|
-
変更不要
|
|
223
|
-
end note
|
|
224
|
-
|
|
225
|
-
@enduml
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
**JavaFX でもヘキサゴナルアーキテクチャを維持する理由:**
|
|
229
|
-
|
|
230
|
-
1. **再利用性**: 既存の UseCase/Repository をそのまま活用
|
|
231
|
-
2. **並行運用**: REST API と JavaFX の両方を提供可能
|
|
232
|
-
3. **テスト容易性**: ドメインロジックは UI フレームワークに依存しない
|
|
233
|
-
4. **移行容易性**: 段階的に Web からデスクトップへ移行可能
|
|
234
|
-
|
|
235
|
-
---
|
|
236
|
-
|
|
237
|
-
### 14.4 ディレクトリ構成
|
|
238
|
-
|
|
239
|
-
既存の構成に `infrastructure/in/javafx/` を追加します。
|
|
240
|
-
|
|
241
|
-
```
|
|
242
|
-
apps/sms/backend/src/main/java/com/example/sms/
|
|
243
|
-
├── domain/ # ドメイン層(API版と共通)
|
|
244
|
-
│ ├── model/
|
|
245
|
-
│ │ ├── product/
|
|
246
|
-
│ │ ├── partner/
|
|
247
|
-
│ │ ├── sales/
|
|
248
|
-
│ │ └── invoice/
|
|
249
|
-
│ └── exception/
|
|
250
|
-
│
|
|
251
|
-
├── application/ # アプリケーション層(API版と共通)
|
|
252
|
-
│ ├── port/
|
|
253
|
-
│ │ ├── in/ # Input Port(ユースケース)
|
|
254
|
-
│ │ └── out/ # Output Port(リポジトリ)
|
|
255
|
-
│ └── service/
|
|
256
|
-
│
|
|
257
|
-
├── infrastructure/
|
|
258
|
-
│ ├── out/
|
|
259
|
-
│ │ └── persistence/ # Output Adapter(DB実装)- 既存
|
|
260
|
-
│ │ ├── mapper/
|
|
261
|
-
│ │ └── repository/
|
|
262
|
-
│ ├── in/
|
|
263
|
-
│ │ ├── rest/ # Input Adapter(REST実装)- 既存
|
|
264
|
-
│ │ │ ├── controller/
|
|
265
|
-
│ │ │ └── dto/
|
|
266
|
-
│ │ └── javafx/ # Input Adapter(JavaFX実装)- 新規追加
|
|
267
|
-
│ │ ├── controller/ # FXML Controller
|
|
268
|
-
│ │ ├── viewmodel/ # ViewModel
|
|
269
|
-
│ │ ├── view/ # 画面表示用の行モデル
|
|
270
|
-
│ │ ├── dialog/ # ダイアログ
|
|
271
|
-
│ │ ├── util/ # ユーティリティ
|
|
272
|
-
│ │ └── config/ # JavaFX 設定
|
|
273
|
-
│
|
|
274
|
-
└── Application.java # Spring Boot + JavaFX 起動クラス
|
|
275
|
-
|
|
276
|
-
apps/sms/backend/src/main/resources/
|
|
277
|
-
├── fxml/ # FXML ファイル
|
|
278
|
-
│ ├── main.fxml
|
|
279
|
-
│ ├── product/
|
|
280
|
-
│ │ ├── product-list.fxml
|
|
281
|
-
│ │ └── product-edit.fxml
|
|
282
|
-
│ ├── order/
|
|
283
|
-
│ │ ├── order-list.fxml
|
|
284
|
-
│ │ └── order-entry.fxml
|
|
285
|
-
│ └── common/
|
|
286
|
-
│ ├── login.fxml
|
|
287
|
-
│ └── error-dialog.fxml
|
|
288
|
-
│
|
|
289
|
-
├── css/ # スタイルシート
|
|
290
|
-
│ ├── application.css
|
|
291
|
-
│ └── theme/
|
|
292
|
-
│
|
|
293
|
-
├── images/ # 画像リソース
|
|
294
|
-
│
|
|
295
|
-
└── reports/ # JasperReports テンプレート
|
|
296
|
-
├── invoice.jrxml
|
|
297
|
-
└── delivery-slip.jrxml
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
---
|
|
301
|
-
|
|
302
|
-
### 14.5 技術スタックの追加
|
|
303
|
-
|
|
304
|
-
#### build.gradle.kts(差分)
|
|
305
|
-
|
|
306
|
-
<details>
|
|
307
|
-
<summary>コード例: build.gradle.kts</summary>
|
|
308
|
-
|
|
309
|
-
```kotlin
|
|
310
|
-
plugins {
|
|
311
|
-
// 既存のプラグイン...
|
|
312
|
-
id("org.openjfx.javafxplugin") version "0.1.0" // 追加
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// JavaFX 設定
|
|
316
|
-
javafx {
|
|
317
|
-
version = "21"
|
|
318
|
-
modules = listOf(
|
|
319
|
-
"javafx.controls",
|
|
320
|
-
"javafx.fxml",
|
|
321
|
-
"javafx.graphics",
|
|
322
|
-
"javafx.swing" // JasperReports 連携用
|
|
323
|
-
)
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
dependencies {
|
|
327
|
-
// 既存の依存関係(Spring Boot, MyBatis, PostgreSQL等)はそのまま
|
|
328
|
-
|
|
329
|
-
// JavaFX 関連を追加
|
|
330
|
-
implementation("org.controlsfx:controlsfx:11.2.0") // 拡張コントロール
|
|
331
|
-
implementation("com.dlsc.formsfx:formsfx-core:11.6.0") // フォーム生成
|
|
332
|
-
implementation("org.kordamp.ikonli:ikonli-javafx:12.3.1") // アイコン
|
|
333
|
-
implementation("org.kordamp.ikonli:ikonli-fontawesome5-pack:12.3.1")
|
|
334
|
-
|
|
335
|
-
// JasperReports(帳票出力)
|
|
336
|
-
implementation("net.sf.jasperreports:jasperreports:6.20.6")
|
|
337
|
-
|
|
338
|
-
// Excel 出力
|
|
339
|
-
implementation("org.apache.poi:poi-ooxml:5.2.5")
|
|
340
|
-
|
|
341
|
-
// TestFX(UI テスト)
|
|
342
|
-
testImplementation("org.testfx:testfx-core:4.0.18")
|
|
343
|
-
testImplementation("org.testfx:testfx-junit5:4.0.18")
|
|
344
|
-
}
|
|
345
|
-
```
|
|
346
|
-
|
|
347
|
-
</details>
|
|
348
|
-
|
|
349
|
-
**追加パッケージの説明:**
|
|
350
|
-
|
|
351
|
-
| パッケージ | 用途 |
|
|
352
|
-
|-----------|------|
|
|
353
|
-
| javafx.controls | 標準 UI コントロール |
|
|
354
|
-
| javafx.fxml | FXML パーサー |
|
|
355
|
-
| ControlsFX | 拡張コントロール(SearchableComboBox等) |
|
|
356
|
-
| Ikonli | アイコンフォント |
|
|
357
|
-
| JasperReports | 帳票出力 |
|
|
358
|
-
| Apache POI | Excel 出力 |
|
|
359
|
-
| TestFX | UI 自動テスト |
|
|
360
|
-
|
|
361
|
-
---
|
|
362
|
-
|
|
363
|
-
### 14.6 Spring Boot + JavaFX 統合
|
|
364
|
-
|
|
365
|
-
#### JavaFxApplication.java
|
|
366
|
-
|
|
367
|
-
<details>
|
|
368
|
-
<summary>コード例: JavaFxApplication.java</summary>
|
|
369
|
-
|
|
370
|
-
```java
|
|
371
|
-
package com.example.sms.infrastructure.in.javafx;
|
|
372
|
-
|
|
373
|
-
import javafx.application.Application;
|
|
374
|
-
import javafx.application.Platform;
|
|
375
|
-
import javafx.stage.Stage;
|
|
376
|
-
import org.springframework.boot.builder.SpringApplicationBuilder;
|
|
377
|
-
import org.springframework.context.ConfigurableApplicationContext;
|
|
378
|
-
|
|
379
|
-
/**
|
|
380
|
-
* JavaFX アプリケーションのエントリーポイント
|
|
381
|
-
* Spring Boot コンテキストと JavaFX を統合
|
|
382
|
-
*/
|
|
383
|
-
public class JavaFxApplication extends Application {
|
|
384
|
-
|
|
385
|
-
private ConfigurableApplicationContext springContext;
|
|
386
|
-
|
|
387
|
-
@Override
|
|
388
|
-
public void init() {
|
|
389
|
-
springContext = new SpringApplicationBuilder(SmsApplication.class).run();
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
@Override
|
|
393
|
-
public void start(Stage primaryStage) {
|
|
394
|
-
StageManager stageManager = springContext.getBean(StageManager.class);
|
|
395
|
-
stageManager.setPrimaryStage(primaryStage);
|
|
396
|
-
stageManager.switchScene(FxmlView.MAIN);
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
@Override
|
|
400
|
-
public void stop() {
|
|
401
|
-
springContext.close();
|
|
402
|
-
Platform.exit();
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
```
|
|
406
|
-
|
|
407
|
-
</details>
|
|
408
|
-
|
|
409
|
-
#### StageManager.java
|
|
410
|
-
|
|
411
|
-
<details>
|
|
412
|
-
<summary>コード例: StageManager.java</summary>
|
|
413
|
-
|
|
414
|
-
```java
|
|
415
|
-
package com.example.sms.infrastructure.in.javafx.config;
|
|
416
|
-
|
|
417
|
-
import javafx.fxml.FXMLLoader;
|
|
418
|
-
import javafx.scene.Parent;
|
|
419
|
-
import javafx.scene.Scene;
|
|
420
|
-
import javafx.stage.Stage;
|
|
421
|
-
import org.springframework.context.ApplicationContext;
|
|
422
|
-
import org.springframework.stereotype.Component;
|
|
423
|
-
|
|
424
|
-
import java.io.IOException;
|
|
425
|
-
import java.util.Objects;
|
|
426
|
-
|
|
427
|
-
/**
|
|
428
|
-
* JavaFX Stage/Scene の管理
|
|
429
|
-
*/
|
|
430
|
-
@Component
|
|
431
|
-
public class StageManager {
|
|
432
|
-
|
|
433
|
-
private final ApplicationContext applicationContext;
|
|
434
|
-
private Stage primaryStage;
|
|
435
|
-
|
|
436
|
-
public StageManager(ApplicationContext applicationContext) {
|
|
437
|
-
this.applicationContext = applicationContext;
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
public void setPrimaryStage(Stage primaryStage) {
|
|
441
|
-
this.primaryStage = primaryStage;
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
public void switchScene(FxmlView view) {
|
|
445
|
-
Parent root = loadView(view.getFxmlPath());
|
|
446
|
-
Scene scene = new Scene(root);
|
|
447
|
-
|
|
448
|
-
scene.getStylesheets().add(
|
|
449
|
-
Objects.requireNonNull(
|
|
450
|
-
getClass().getResource("/css/application.css")
|
|
451
|
-
).toExternalForm()
|
|
452
|
-
);
|
|
453
|
-
|
|
454
|
-
primaryStage.setTitle(view.getTitle());
|
|
455
|
-
primaryStage.setScene(scene);
|
|
456
|
-
primaryStage.centerOnScreen();
|
|
457
|
-
|
|
458
|
-
if (!primaryStage.isShowing()) {
|
|
459
|
-
primaryStage.show();
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
private Parent loadView(String fxmlPath) {
|
|
464
|
-
try {
|
|
465
|
-
FXMLLoader loader = new FXMLLoader(getClass().getResource(fxmlPath));
|
|
466
|
-
loader.setControllerFactory(applicationContext::getBean);
|
|
467
|
-
return loader.load();
|
|
468
|
-
} catch (IOException e) {
|
|
469
|
-
throw new RuntimeException("FXML ロードエラー: " + fxmlPath, e);
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
public ApplicationContext getApplicationContext() {
|
|
474
|
-
return applicationContext;
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
```
|
|
478
|
-
|
|
479
|
-
</details>
|
|
480
|
-
|
|
481
|
-
#### FxmlView.java(画面定義)
|
|
482
|
-
|
|
483
|
-
<details>
|
|
484
|
-
<summary>コード例: FxmlView.java</summary>
|
|
485
|
-
|
|
486
|
-
```java
|
|
487
|
-
package com.example.sms.infrastructure.in.javafx.config;
|
|
488
|
-
|
|
489
|
-
public enum FxmlView {
|
|
490
|
-
|
|
491
|
-
MAIN("/fxml/main.fxml", "販売管理システム"),
|
|
492
|
-
PRODUCT_LIST("/fxml/product/product-list.fxml", "商品マスタ"),
|
|
493
|
-
ORDER_ENTRY("/fxml/order/order-entry.fxml", "受注入力"),
|
|
494
|
-
ORDER_LIST("/fxml/order/order-list.fxml", "受注一覧"),
|
|
495
|
-
SHIPMENT_LIST("/fxml/shipment/shipment-list.fxml", "出荷一覧"),
|
|
496
|
-
INVOICE_LIST("/fxml/invoice/invoice-list.fxml", "請求一覧");
|
|
497
|
-
|
|
498
|
-
private final String fxmlPath;
|
|
499
|
-
private final String title;
|
|
500
|
-
|
|
501
|
-
FxmlView(String fxmlPath, String title) {
|
|
502
|
-
this.fxmlPath = fxmlPath;
|
|
503
|
-
this.title = title;
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
public String getFxmlPath() { return fxmlPath; }
|
|
507
|
-
public String getTitle() { return title; }
|
|
508
|
-
}
|
|
509
|
-
```
|
|
510
|
-
|
|
511
|
-
</details>
|
|
512
|
-
|
|
513
|
-
---
|
|
514
|
-
|
|
515
|
-
### 14.7 JavaFX Property の活用
|
|
516
|
-
|
|
517
|
-
JavaFX の Property 機能を使って、UI とデータの双方向バインディングを実現します。
|
|
518
|
-
|
|
519
|
-
#### ProductRow.java(View 用データクラス)
|
|
520
|
-
|
|
521
|
-
<details>
|
|
522
|
-
<summary>コード例: ProductRow.java</summary>
|
|
523
|
-
|
|
524
|
-
```java
|
|
525
|
-
package com.example.sms.infrastructure.in.javafx.view;
|
|
526
|
-
|
|
527
|
-
import javafx.beans.property.*;
|
|
528
|
-
import java.math.BigDecimal;
|
|
529
|
-
|
|
530
|
-
/**
|
|
531
|
-
* 商品マスタ画面用の行モデル
|
|
532
|
-
*/
|
|
533
|
-
public class ProductRow {
|
|
534
|
-
|
|
535
|
-
private final StringProperty productCode = new SimpleStringProperty();
|
|
536
|
-
private final StringProperty productName = new SimpleStringProperty();
|
|
537
|
-
private final StringProperty categoryName = new SimpleStringProperty();
|
|
538
|
-
private final ObjectProperty<BigDecimal> unitPrice = new SimpleObjectProperty<>();
|
|
539
|
-
private final BooleanProperty discontinued = new SimpleBooleanProperty();
|
|
540
|
-
|
|
541
|
-
// Property アクセサ
|
|
542
|
-
public StringProperty productCodeProperty() { return productCode; }
|
|
543
|
-
public StringProperty productNameProperty() { return productName; }
|
|
544
|
-
public StringProperty categoryNameProperty() { return categoryName; }
|
|
545
|
-
public ObjectProperty<BigDecimal> unitPriceProperty() { return unitPrice; }
|
|
546
|
-
public BooleanProperty discontinuedProperty() { return discontinued; }
|
|
547
|
-
|
|
548
|
-
// Getter/Setter
|
|
549
|
-
public String getProductCode() { return productCode.get(); }
|
|
550
|
-
public void setProductCode(String value) { productCode.set(value); }
|
|
551
|
-
|
|
552
|
-
public String getProductName() { return productName.get(); }
|
|
553
|
-
public void setProductName(String value) { productName.set(value); }
|
|
554
|
-
|
|
555
|
-
public BigDecimal getUnitPrice() { return unitPrice.get(); }
|
|
556
|
-
public void setUnitPrice(BigDecimal value) { unitPrice.set(value); }
|
|
557
|
-
}
|
|
558
|
-
```
|
|
559
|
-
|
|
560
|
-
</details>
|
|
561
|
-
|
|
562
|
-
#### ProductViewModel.java
|
|
563
|
-
|
|
564
|
-
<details>
|
|
565
|
-
<summary>コード例: ProductViewModel.java</summary>
|
|
566
|
-
|
|
567
|
-
```java
|
|
568
|
-
package com.example.sms.infrastructure.in.javafx.viewmodel;
|
|
569
|
-
|
|
570
|
-
import com.example.sms.application.port.in.ProductUseCase;
|
|
571
|
-
import com.example.sms.domain.model.product.Product;
|
|
572
|
-
import com.example.sms.infrastructure.in.javafx.view.ProductRow;
|
|
573
|
-
import javafx.beans.property.*;
|
|
574
|
-
import javafx.collections.FXCollections;
|
|
575
|
-
import javafx.collections.ObservableList;
|
|
576
|
-
import org.springframework.stereotype.Component;
|
|
577
|
-
|
|
578
|
-
import java.util.List;
|
|
579
|
-
|
|
580
|
-
@Component
|
|
581
|
-
public class ProductViewModel {
|
|
582
|
-
|
|
583
|
-
private final ProductUseCase productUseCase;
|
|
584
|
-
|
|
585
|
-
private final ObservableList<ProductRow> products = FXCollections.observableArrayList();
|
|
586
|
-
private final ObjectProperty<ProductRow> selectedProduct = new SimpleObjectProperty<>();
|
|
587
|
-
private final StringProperty searchKeyword = new SimpleStringProperty("");
|
|
588
|
-
private final BooleanProperty loading = new SimpleBooleanProperty(false);
|
|
589
|
-
private final StringProperty errorMessage = new SimpleStringProperty();
|
|
590
|
-
|
|
591
|
-
public ProductViewModel(ProductUseCase productUseCase) {
|
|
592
|
-
this.productUseCase = productUseCase;
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
public void search() {
|
|
596
|
-
loading.set(true);
|
|
597
|
-
errorMessage.set(null);
|
|
598
|
-
|
|
599
|
-
try {
|
|
600
|
-
List<Product> result = searchKeyword.get().isBlank()
|
|
601
|
-
? productUseCase.findAll()
|
|
602
|
-
: productUseCase.searchByKeyword(searchKeyword.get());
|
|
603
|
-
|
|
604
|
-
products.clear();
|
|
605
|
-
result.forEach(p -> products.add(toRow(p)));
|
|
606
|
-
} catch (Exception e) {
|
|
607
|
-
errorMessage.set("検索エラー: " + e.getMessage());
|
|
608
|
-
} finally {
|
|
609
|
-
loading.set(false);
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
public void delete(String productCode) {
|
|
614
|
-
loading.set(true);
|
|
615
|
-
try {
|
|
616
|
-
productUseCase.delete(productCode);
|
|
617
|
-
products.removeIf(row -> row.getProductCode().equals(productCode));
|
|
618
|
-
} catch (Exception e) {
|
|
619
|
-
errorMessage.set("削除エラー: " + e.getMessage());
|
|
620
|
-
throw e;
|
|
621
|
-
} finally {
|
|
622
|
-
loading.set(false);
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
public ObservableList<ProductRow> getProducts() { return products; }
|
|
627
|
-
public StringProperty searchKeywordProperty() { return searchKeyword; }
|
|
628
|
-
public BooleanProperty loadingProperty() { return loading; }
|
|
629
|
-
|
|
630
|
-
private ProductRow toRow(Product product) {
|
|
631
|
-
ProductRow row = new ProductRow();
|
|
632
|
-
row.setProductCode(product.getProductCode());
|
|
633
|
-
row.setProductName(product.getProductName());
|
|
634
|
-
row.setUnitPrice(product.getUnitPrice());
|
|
635
|
-
return row;
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
```
|
|
639
|
-
|
|
640
|
-
</details>
|
|
641
|
-
|
|
642
|
-
---
|
|
643
|
-
|
|
644
|
-
## 第15章:マスタの実装
|
|
645
|
-
|
|
646
|
-
### 15.1 商品マスタ画面の設計
|
|
647
|
-
|
|
648
|
-
```plantuml
|
|
649
|
-
@startuml product_master_screen
|
|
650
|
-
|
|
651
|
-
package "商品マスタ画面構成" {
|
|
652
|
-
|
|
653
|
-
class "ProductListController\n(一覧画面)" as list {
|
|
654
|
-
+ TableView<ProductRow>
|
|
655
|
-
+ TextField (検索)
|
|
656
|
-
+ Button (新規/編集/削除)
|
|
657
|
-
--
|
|
658
|
-
initialize()
|
|
659
|
-
onSearch()
|
|
660
|
-
onNew()
|
|
661
|
-
onEdit()
|
|
662
|
-
onDelete()
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
class "ProductEditController\n(編集画面)" as edit {
|
|
666
|
-
+ TextField (コード/名称)
|
|
667
|
-
+ ComboBox (分類)
|
|
668
|
-
+ TextField (単価)
|
|
669
|
-
--
|
|
670
|
-
initialize()
|
|
671
|
-
onSave()
|
|
672
|
-
onCancel()
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
class "ProductViewModel\n(画面状態)" as vm {
|
|
676
|
-
+ ObservableList<ProductRow>
|
|
677
|
-
+ selectedProduct
|
|
678
|
-
+ searchKeyword
|
|
679
|
-
--
|
|
680
|
-
search()
|
|
681
|
-
create()
|
|
682
|
-
update()
|
|
683
|
-
delete()
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
class "ProductUseCase\n(ビジネスロジック)" as usecase {
|
|
687
|
-
+ findAll()
|
|
688
|
-
+ findByCode()
|
|
689
|
-
+ create()
|
|
690
|
-
+ update()
|
|
691
|
-
+ delete()
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
list --> vm : "バインド"
|
|
696
|
-
edit --> vm : "バインド"
|
|
697
|
-
vm --> usecase : "Input Port"
|
|
698
|
-
|
|
699
|
-
@enduml
|
|
700
|
-
```
|
|
701
|
-
|
|
702
|
-
### 15.2 商品マスタ一覧画面の TDD 実装
|
|
703
|
-
|
|
704
|
-
#### Red: 失敗するテストを書く
|
|
705
|
-
|
|
706
|
-
<details>
|
|
707
|
-
<summary>コード例: ProductListControllerTest.java</summary>
|
|
708
|
-
|
|
709
|
-
```java
|
|
710
|
-
@ExtendWith({ApplicationExtension.class, MockitoExtension.class})
|
|
711
|
-
@DisplayName("商品マスタ一覧画面")
|
|
712
|
-
class ProductListControllerTest {
|
|
713
|
-
|
|
714
|
-
@Mock
|
|
715
|
-
private ProductUseCase productUseCase;
|
|
716
|
-
|
|
717
|
-
private ProductViewModel viewModel;
|
|
718
|
-
|
|
719
|
-
@Start
|
|
720
|
-
void start(Stage stage) throws Exception {
|
|
721
|
-
viewModel = new ProductViewModel(productUseCase);
|
|
722
|
-
var controller = new ProductListController(viewModel);
|
|
723
|
-
|
|
724
|
-
var loader = new FXMLLoader(getClass().getResource("/fxml/product/product-list.fxml"));
|
|
725
|
-
loader.setController(controller);
|
|
726
|
-
stage.setScene(new Scene(loader.load()));
|
|
727
|
-
stage.show();
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
@Test
|
|
731
|
-
@DisplayName("商品一覧がテーブルに表示される")
|
|
732
|
-
void shouldDisplayProductsInTable(FxRobot robot) {
|
|
733
|
-
// Arrange
|
|
734
|
-
when(productUseCase.findAll()).thenReturn(List.of(
|
|
735
|
-
createProduct("P001", "テスト商品A", new BigDecimal("500")),
|
|
736
|
-
createProduct("P002", "テスト商品B", new BigDecimal("800"))
|
|
737
|
-
));
|
|
738
|
-
|
|
739
|
-
// Act
|
|
740
|
-
robot.interact(() -> viewModel.search());
|
|
741
|
-
|
|
742
|
-
// Assert
|
|
743
|
-
TableView<ProductRow> table = robot.lookup("#productTable").queryTableView();
|
|
744
|
-
assertThat(table.getItems()).hasSize(2);
|
|
745
|
-
assertThat(table.getItems().get(0).getProductCode()).isEqualTo("P001");
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
@Test
|
|
749
|
-
@DisplayName("キーワードで商品を検索できる")
|
|
750
|
-
void shouldSearchProductsByKeyword(FxRobot robot) {
|
|
751
|
-
when(productUseCase.searchByKeyword("テスト")).thenReturn(List.of(
|
|
752
|
-
createProduct("P001", "テスト商品A", new BigDecimal("500"))
|
|
753
|
-
));
|
|
754
|
-
|
|
755
|
-
robot.clickOn("#searchField").write("テスト").type(KeyCode.ENTER);
|
|
756
|
-
|
|
757
|
-
TableView<ProductRow> table = robot.lookup("#productTable").queryTableView();
|
|
758
|
-
assertThat(table.getItems()).hasSize(1);
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
```
|
|
762
|
-
|
|
763
|
-
</details>
|
|
764
|
-
|
|
765
|
-
#### product-list.fxml
|
|
766
|
-
|
|
767
|
-
<details>
|
|
768
|
-
<summary>コード例: product-list.fxml</summary>
|
|
769
|
-
|
|
770
|
-
```xml
|
|
771
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
772
|
-
<?import javafx.scene.control.*?>
|
|
773
|
-
<?import javafx.scene.layout.*?>
|
|
774
|
-
<?import org.kordamp.ikonli.javafx.FontIcon?>
|
|
775
|
-
|
|
776
|
-
<VBox xmlns="http://javafx.com/javafx/21"
|
|
777
|
-
xmlns:fx="http://javafx.com/fxml/1"
|
|
778
|
-
fx:controller="com.example.sms.infrastructure.in.javafx.controller.ProductListController"
|
|
779
|
-
spacing="10">
|
|
780
|
-
|
|
781
|
-
<!-- ツールバー -->
|
|
782
|
-
<HBox spacing="10" alignment="CENTER_LEFT">
|
|
783
|
-
<TextField fx:id="searchField" promptText="商品コード・商品名で検索" prefWidth="300"/>
|
|
784
|
-
<Button onAction="#onSearch">
|
|
785
|
-
<graphic><FontIcon iconLiteral="fas-search"/></graphic>
|
|
786
|
-
</Button>
|
|
787
|
-
<Pane HBox.hgrow="ALWAYS"/>
|
|
788
|
-
<Button fx:id="newButton" text="新規" styleClass="primary" onAction="#onNew">
|
|
789
|
-
<graphic><FontIcon iconLiteral="fas-plus"/></graphic>
|
|
790
|
-
</Button>
|
|
791
|
-
<Button fx:id="editButton" text="編集" onAction="#onEdit" disable="true"/>
|
|
792
|
-
<Button fx:id="deleteButton" text="削除" styleClass="danger" onAction="#onDelete" disable="true"/>
|
|
793
|
-
<Button text="Excel出力" onAction="#onExportExcel">
|
|
794
|
-
<graphic><FontIcon iconLiteral="fas-file-excel"/></graphic>
|
|
795
|
-
</Button>
|
|
796
|
-
</HBox>
|
|
797
|
-
|
|
798
|
-
<!-- 商品テーブル -->
|
|
799
|
-
<TableView fx:id="productTable" VBox.vgrow="ALWAYS">
|
|
800
|
-
<placeholder><Label text="該当する商品がありません"/></placeholder>
|
|
801
|
-
<columns>
|
|
802
|
-
<TableColumn text="商品コード" prefWidth="100">
|
|
803
|
-
<cellValueFactory><PropertyValueFactory property="productCode"/></cellValueFactory>
|
|
804
|
-
</TableColumn>
|
|
805
|
-
<TableColumn text="商品名" prefWidth="200">
|
|
806
|
-
<cellValueFactory><PropertyValueFactory property="productName"/></cellValueFactory>
|
|
807
|
-
</TableColumn>
|
|
808
|
-
<TableColumn text="単価" prefWidth="100" styleClass="amount-column">
|
|
809
|
-
<cellValueFactory><PropertyValueFactory property="unitPrice"/></cellValueFactory>
|
|
810
|
-
</TableColumn>
|
|
811
|
-
</columns>
|
|
812
|
-
</TableView>
|
|
813
|
-
|
|
814
|
-
<!-- ステータスバー -->
|
|
815
|
-
<HBox spacing="10" styleClass="status-bar">
|
|
816
|
-
<Label fx:id="recordCount" text="0 件"/>
|
|
817
|
-
<Pane HBox.hgrow="ALWAYS"/>
|
|
818
|
-
<ProgressIndicator fx:id="loadingIndicator" visible="false" prefWidth="20" prefHeight="20"/>
|
|
819
|
-
</HBox>
|
|
820
|
-
</VBox>
|
|
821
|
-
```
|
|
822
|
-
|
|
823
|
-
</details>
|
|
824
|
-
|
|
825
|
-
---
|
|
826
|
-
|
|
827
|
-
## 第16章:トランザクションの実装
|
|
828
|
-
|
|
829
|
-
### 16.1 トランザクション画面の特徴
|
|
830
|
-
|
|
831
|
-
トランザクション画面は、マスタ画面とは異なる以下の特徴があります。
|
|
832
|
-
|
|
833
|
-
```plantuml
|
|
834
|
-
@startuml transaction_screen_pattern
|
|
835
|
-
|
|
836
|
-
package "トランザクション画面の特徴" {
|
|
837
|
-
|
|
838
|
-
class "ヘッダ・明細構造" as header_detail {
|
|
839
|
-
+ 受注ヘッダ(顧客、日付)
|
|
840
|
-
+ 受注明細(商品、数量、単価)
|
|
841
|
-
--
|
|
842
|
-
1:N の関係
|
|
843
|
-
明細の動的追加・削除
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
class "計算処理" as calculation {
|
|
847
|
-
+ 明細金額 = 数量 × 単価
|
|
848
|
-
+ 合計金額 = Σ 明細金額
|
|
849
|
-
+ 税額計算
|
|
850
|
-
--
|
|
851
|
-
リアルタイム再計算
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
class "ステータス管理" as status {
|
|
855
|
-
+ 受注 → 出荷 → 売上
|
|
856
|
-
+ ステータスによる編集制限
|
|
857
|
-
--
|
|
858
|
-
業務フロー制御
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
class "帳票出力" as report {
|
|
862
|
-
+ 納品書
|
|
863
|
-
+ 請求書
|
|
864
|
-
--
|
|
865
|
-
JasperReports 連携
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
header_detail --> calculation : "明細変更時"
|
|
870
|
-
status --> header_detail : "編集可否"
|
|
871
|
-
header_detail --> report : "印刷"
|
|
872
|
-
|
|
873
|
-
@enduml
|
|
874
|
-
```
|
|
875
|
-
|
|
876
|
-
### 16.2 受注入力画面の設計
|
|
877
|
-
|
|
878
|
-
```plantuml
|
|
879
|
-
@startuml order_entry_screen
|
|
880
|
-
|
|
881
|
-
package "受注入力画面" {
|
|
882
|
-
|
|
883
|
-
class "OrderEntryController" as controller {
|
|
884
|
-
+ customerComboBox
|
|
885
|
-
+ orderDatePicker
|
|
886
|
-
+ detailTableView
|
|
887
|
-
+ totalAmountLabel
|
|
888
|
-
--
|
|
889
|
-
onCustomerChange()
|
|
890
|
-
onAddDetail()
|
|
891
|
-
onRemoveDetail()
|
|
892
|
-
onSave()
|
|
893
|
-
onPrint()
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
class "OrderViewModel" as vm {
|
|
897
|
-
+ selectedCustomer
|
|
898
|
-
+ orderDate
|
|
899
|
-
+ orderDetails (ObservableList)
|
|
900
|
-
+ totalAmount (calculated)
|
|
901
|
-
--
|
|
902
|
-
addDetail()
|
|
903
|
-
removeDetail()
|
|
904
|
-
calculateTotal()
|
|
905
|
-
save()
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
class "OrderDetailRow" as detail {
|
|
909
|
-
+ productCode
|
|
910
|
-
+ productName
|
|
911
|
-
+ quantity
|
|
912
|
-
+ unitPrice
|
|
913
|
-
+ amount (calculated)
|
|
914
|
-
}
|
|
915
|
-
}
|
|
916
|
-
|
|
917
|
-
controller --> vm : "バインド"
|
|
918
|
-
vm "1" --> "*" detail : "contains"
|
|
919
|
-
|
|
920
|
-
note right of detail
|
|
921
|
-
明細行の金額は
|
|
922
|
-
数量 × 単価 で
|
|
923
|
-
自動計算
|
|
924
|
-
end note
|
|
925
|
-
|
|
926
|
-
@enduml
|
|
927
|
-
```
|
|
928
|
-
|
|
929
|
-
### 16.3 受注入力画面の TDD 実装
|
|
930
|
-
|
|
931
|
-
#### Red: 失敗するテストを書く
|
|
932
|
-
|
|
933
|
-
<details>
|
|
934
|
-
<summary>コード例: OrderEntryControllerTest.java</summary>
|
|
935
|
-
|
|
936
|
-
```java
|
|
937
|
-
@ExtendWith({ApplicationExtension.class, MockitoExtension.class})
|
|
938
|
-
@DisplayName("受注入力画面")
|
|
939
|
-
class OrderEntryControllerTest {
|
|
940
|
-
|
|
941
|
-
@Mock
|
|
942
|
-
private OrderUseCase orderUseCase;
|
|
943
|
-
@Mock
|
|
944
|
-
private ProductUseCase productUseCase;
|
|
945
|
-
|
|
946
|
-
private OrderViewModel viewModel;
|
|
947
|
-
|
|
948
|
-
@Nested
|
|
949
|
-
@DisplayName("明細追加")
|
|
950
|
-
class AddDetail {
|
|
951
|
-
|
|
952
|
-
@Test
|
|
953
|
-
@DisplayName("商品を選択して明細を追加できる")
|
|
954
|
-
void shouldAddDetailWhenProductSelected(FxRobot robot) {
|
|
955
|
-
when(productUseCase.findByCode("P001")).thenReturn(
|
|
956
|
-
createProduct("P001", "テスト商品", new BigDecimal("500"))
|
|
957
|
-
);
|
|
958
|
-
|
|
959
|
-
robot.clickOn("#productCodeField").write("P001");
|
|
960
|
-
robot.clickOn("#quantityField").write("10");
|
|
961
|
-
robot.clickOn("#addDetailButton");
|
|
962
|
-
|
|
963
|
-
TableView<OrderDetailRow> table = robot.lookup("#detailTable").queryTableView();
|
|
964
|
-
assertThat(table.getItems()).hasSize(1);
|
|
965
|
-
assertThat(table.getItems().get(0).getAmount())
|
|
966
|
-
.isEqualByComparingTo(new BigDecimal("5000"));
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
|
|
970
|
-
@Nested
|
|
971
|
-
@DisplayName("合計計算")
|
|
972
|
-
class TotalCalculation {
|
|
973
|
-
|
|
974
|
-
@Test
|
|
975
|
-
@DisplayName("明細追加時に合計金額が再計算される")
|
|
976
|
-
void shouldRecalculateTotalWhenDetailAdded(FxRobot robot) {
|
|
977
|
-
viewModel.addDetail(createDetailRow("P001", 10, new BigDecimal("500")));
|
|
978
|
-
viewModel.addDetail(createDetailRow("P002", 5, new BigDecimal("800")));
|
|
979
|
-
|
|
980
|
-
Label totalLabel = robot.lookup("#totalAmountLabel").queryAs(Label.class);
|
|
981
|
-
// 10 × 500 + 5 × 800 = 9000
|
|
982
|
-
assertThat(totalLabel.getText()).contains("9,000");
|
|
983
|
-
}
|
|
984
|
-
}
|
|
985
|
-
}
|
|
986
|
-
```
|
|
987
|
-
|
|
988
|
-
</details>
|
|
989
|
-
|
|
990
|
-
#### order-entry.fxml
|
|
991
|
-
|
|
992
|
-
<details>
|
|
993
|
-
<summary>コード例: order-entry.fxml</summary>
|
|
994
|
-
|
|
995
|
-
```xml
|
|
996
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
997
|
-
<?import javafx.scene.control.*?>
|
|
998
|
-
<?import javafx.scene.layout.*?>
|
|
999
|
-
<?import org.kordamp.ikonli.javafx.FontIcon?>
|
|
1000
|
-
|
|
1001
|
-
<VBox xmlns="http://javafx.com/javafx/21"
|
|
1002
|
-
xmlns:fx="http://javafx.com/fxml/1"
|
|
1003
|
-
fx:controller="com.example.sms.infrastructure.in.javafx.controller.OrderEntryController"
|
|
1004
|
-
spacing="10">
|
|
1005
|
-
|
|
1006
|
-
<!-- ヘッダ情報 -->
|
|
1007
|
-
<GridPane hgap="10" vgap="10" styleClass="header-form">
|
|
1008
|
-
<Label text="受注番号:" GridPane.columnIndex="0" GridPane.rowIndex="0"/>
|
|
1009
|
-
<TextField fx:id="orderNumberField" editable="false" GridPane.columnIndex="1"/>
|
|
1010
|
-
|
|
1011
|
-
<Label text="受注日:" GridPane.columnIndex="2" GridPane.rowIndex="0"/>
|
|
1012
|
-
<DatePicker fx:id="orderDatePicker" GridPane.columnIndex="3"/>
|
|
1013
|
-
|
|
1014
|
-
<Label text="顧客:" GridPane.columnIndex="0" GridPane.rowIndex="1"/>
|
|
1015
|
-
<HBox spacing="5" GridPane.columnIndex="1" GridPane.columnSpan="3">
|
|
1016
|
-
<TextField fx:id="customerCodeField" prefWidth="100"/>
|
|
1017
|
-
<Button onAction="#onSearchCustomer">
|
|
1018
|
-
<graphic><FontIcon iconLiteral="fas-search"/></graphic>
|
|
1019
|
-
</Button>
|
|
1020
|
-
<TextField fx:id="customerNameField" editable="false" HBox.hgrow="ALWAYS"/>
|
|
1021
|
-
</HBox>
|
|
1022
|
-
</GridPane>
|
|
1023
|
-
|
|
1024
|
-
<Separator/>
|
|
1025
|
-
|
|
1026
|
-
<!-- 明細入力エリア -->
|
|
1027
|
-
<HBox spacing="10" alignment="CENTER_LEFT">
|
|
1028
|
-
<Label text="商品:"/>
|
|
1029
|
-
<TextField fx:id="productCodeField" prefWidth="100"/>
|
|
1030
|
-
<TextField fx:id="productNameField" prefWidth="200" editable="false"/>
|
|
1031
|
-
<Label text="数量:"/>
|
|
1032
|
-
<TextField fx:id="quantityField" prefWidth="80" alignment="CENTER_RIGHT"/>
|
|
1033
|
-
<Label text="単価:"/>
|
|
1034
|
-
<TextField fx:id="unitPriceField" prefWidth="100" alignment="CENTER_RIGHT"/>
|
|
1035
|
-
<Button fx:id="addDetailButton" text="追加" styleClass="primary" onAction="#onAddDetail">
|
|
1036
|
-
<graphic><FontIcon iconLiteral="fas-plus"/></graphic>
|
|
1037
|
-
</Button>
|
|
1038
|
-
</HBox>
|
|
1039
|
-
|
|
1040
|
-
<!-- 明細テーブル -->
|
|
1041
|
-
<TableView fx:id="detailTable" VBox.vgrow="ALWAYS" editable="true">
|
|
1042
|
-
<columns>
|
|
1043
|
-
<TableColumn text="No." prefWidth="50"/>
|
|
1044
|
-
<TableColumn text="商品コード" prefWidth="100"/>
|
|
1045
|
-
<TableColumn text="商品名" prefWidth="200"/>
|
|
1046
|
-
<TableColumn text="数量" prefWidth="80" styleClass="quantity-column"/>
|
|
1047
|
-
<TableColumn text="単価" prefWidth="100" styleClass="amount-column"/>
|
|
1048
|
-
<TableColumn text="金額" prefWidth="120" styleClass="amount-column"/>
|
|
1049
|
-
<TableColumn text="削除" prefWidth="60"/>
|
|
1050
|
-
</columns>
|
|
1051
|
-
</TableView>
|
|
1052
|
-
|
|
1053
|
-
<!-- 合計エリア -->
|
|
1054
|
-
<HBox spacing="20" alignment="CENTER_RIGHT" styleClass="total-area">
|
|
1055
|
-
<VBox alignment="CENTER_RIGHT" spacing="5">
|
|
1056
|
-
<HBox spacing="10" alignment="CENTER_RIGHT">
|
|
1057
|
-
<Label text="小計:"/>
|
|
1058
|
-
<Label fx:id="subtotalLabel" text="¥0"/>
|
|
1059
|
-
</HBox>
|
|
1060
|
-
<HBox spacing="10" alignment="CENTER_RIGHT">
|
|
1061
|
-
<Label text="消費税:"/>
|
|
1062
|
-
<Label fx:id="taxAmountLabel" text="¥0"/>
|
|
1063
|
-
</HBox>
|
|
1064
|
-
<Separator/>
|
|
1065
|
-
<HBox spacing="10" alignment="CENTER_RIGHT">
|
|
1066
|
-
<Label text="合計金額:" styleClass="total-label-large"/>
|
|
1067
|
-
<Label fx:id="totalAmountLabel" text="¥0" styleClass="total-value-large"/>
|
|
1068
|
-
</HBox>
|
|
1069
|
-
</VBox>
|
|
1070
|
-
</HBox>
|
|
1071
|
-
|
|
1072
|
-
<!-- ボタンエリア -->
|
|
1073
|
-
<HBox spacing="10" alignment="CENTER_RIGHT">
|
|
1074
|
-
<Button text="クリア" onAction="#onClear"/>
|
|
1075
|
-
<Button text="印刷" onAction="#onPrint">
|
|
1076
|
-
<graphic><FontIcon iconLiteral="fas-print"/></graphic>
|
|
1077
|
-
</Button>
|
|
1078
|
-
<Button fx:id="saveButton" text="登録" styleClass="primary" onAction="#onSave">
|
|
1079
|
-
<graphic><FontIcon iconLiteral="fas-save"/></graphic>
|
|
1080
|
-
</Button>
|
|
1081
|
-
</HBox>
|
|
1082
|
-
</VBox>
|
|
1083
|
-
```
|
|
1084
|
-
|
|
1085
|
-
</details>
|
|
1086
|
-
|
|
1087
|
-
---
|
|
1088
|
-
|
|
1089
|
-
## 第17章:エラーハンドリングとドキュメント
|
|
1090
|
-
|
|
1091
|
-
### 17.1 AlertHelper の実装
|
|
1092
|
-
|
|
1093
|
-
アラートダイアログを統一的に扱うためのヘルパークラスを作成します。
|
|
1094
|
-
|
|
1095
|
-
<details>
|
|
1096
|
-
<summary>コード例: AlertHelper.java</summary>
|
|
1097
|
-
|
|
1098
|
-
```java
|
|
1099
|
-
package com.example.sms.infrastructure.in.javafx.util;
|
|
1100
|
-
|
|
1101
|
-
import javafx.scene.control.Alert;
|
|
1102
|
-
import javafx.scene.control.ButtonType;
|
|
1103
|
-
import javafx.scene.control.TextArea;
|
|
1104
|
-
import javafx.scene.layout.GridPane;
|
|
1105
|
-
import javafx.scene.layout.Priority;
|
|
1106
|
-
|
|
1107
|
-
import java.io.PrintWriter;
|
|
1108
|
-
import java.io.StringWriter;
|
|
1109
|
-
import java.util.Optional;
|
|
1110
|
-
|
|
1111
|
-
public class AlertHelper {
|
|
1112
|
-
|
|
1113
|
-
private AlertHelper() {}
|
|
1114
|
-
|
|
1115
|
-
public static void showInfo(String message) {
|
|
1116
|
-
Alert alert = new Alert(Alert.AlertType.INFORMATION);
|
|
1117
|
-
alert.setTitle("情報");
|
|
1118
|
-
alert.setHeaderText(null);
|
|
1119
|
-
alert.setContentText(message);
|
|
1120
|
-
alert.showAndWait();
|
|
1121
|
-
}
|
|
1122
|
-
|
|
1123
|
-
public static void showWarning(String message) {
|
|
1124
|
-
Alert alert = new Alert(Alert.AlertType.WARNING);
|
|
1125
|
-
alert.setTitle("警告");
|
|
1126
|
-
alert.setHeaderText(null);
|
|
1127
|
-
alert.setContentText(message);
|
|
1128
|
-
alert.showAndWait();
|
|
1129
|
-
}
|
|
1130
|
-
|
|
1131
|
-
public static void showError(String message) {
|
|
1132
|
-
Alert alert = new Alert(Alert.AlertType.ERROR);
|
|
1133
|
-
alert.setTitle("エラー");
|
|
1134
|
-
alert.setHeaderText(null);
|
|
1135
|
-
alert.setContentText(message);
|
|
1136
|
-
alert.showAndWait();
|
|
1137
|
-
}
|
|
1138
|
-
|
|
1139
|
-
public static void showException(String title, String header, Throwable exception) {
|
|
1140
|
-
Alert alert = new Alert(Alert.AlertType.ERROR);
|
|
1141
|
-
alert.setTitle(title);
|
|
1142
|
-
alert.setHeaderText(header);
|
|
1143
|
-
alert.setContentText(exception.getMessage());
|
|
1144
|
-
|
|
1145
|
-
StringWriter sw = new StringWriter();
|
|
1146
|
-
PrintWriter pw = new PrintWriter(sw);
|
|
1147
|
-
exception.printStackTrace(pw);
|
|
1148
|
-
|
|
1149
|
-
TextArea textArea = new TextArea(sw.toString());
|
|
1150
|
-
textArea.setEditable(false);
|
|
1151
|
-
textArea.setWrapText(true);
|
|
1152
|
-
textArea.setMaxWidth(Double.MAX_VALUE);
|
|
1153
|
-
textArea.setMaxHeight(Double.MAX_VALUE);
|
|
1154
|
-
GridPane.setVgrow(textArea, Priority.ALWAYS);
|
|
1155
|
-
GridPane.setHgrow(textArea, Priority.ALWAYS);
|
|
1156
|
-
|
|
1157
|
-
GridPane expContent = new GridPane();
|
|
1158
|
-
expContent.setMaxWidth(Double.MAX_VALUE);
|
|
1159
|
-
expContent.add(textArea, 0, 0);
|
|
1160
|
-
|
|
1161
|
-
alert.getDialogPane().setExpandableContent(expContent);
|
|
1162
|
-
alert.showAndWait();
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
|
-
public static boolean showConfirmation(String message) {
|
|
1166
|
-
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
|
|
1167
|
-
alert.setTitle("確認");
|
|
1168
|
-
alert.setHeaderText(null);
|
|
1169
|
-
alert.setContentText(message);
|
|
1170
|
-
|
|
1171
|
-
Optional<ButtonType> result = alert.showAndWait();
|
|
1172
|
-
return result.isPresent() && result.get() == ButtonType.OK;
|
|
1173
|
-
}
|
|
1174
|
-
}
|
|
1175
|
-
```
|
|
1176
|
-
|
|
1177
|
-
</details>
|
|
1178
|
-
|
|
1179
|
-
### 17.2 グローバル例外ハンドラ
|
|
1180
|
-
|
|
1181
|
-
<details>
|
|
1182
|
-
<summary>コード例: グローバル例外ハンドラ</summary>
|
|
1183
|
-
|
|
1184
|
-
```java
|
|
1185
|
-
public class JavaFxApplication extends Application {
|
|
1186
|
-
|
|
1187
|
-
private static final Logger logger = LoggerFactory.getLogger(JavaFxApplication.class);
|
|
1188
|
-
|
|
1189
|
-
@Override
|
|
1190
|
-
public void init() {
|
|
1191
|
-
Thread.setDefaultUncaughtExceptionHandler(this::handleUncaughtException);
|
|
1192
|
-
// ...
|
|
1193
|
-
}
|
|
1194
|
-
|
|
1195
|
-
private void handleUncaughtException(Thread thread, Throwable throwable) {
|
|
1196
|
-
logger.error("未処理例外が発生しました [Thread: {}]", thread.getName(), throwable);
|
|
1197
|
-
|
|
1198
|
-
if (Platform.isFxApplicationThread()) {
|
|
1199
|
-
showErrorDialog(throwable);
|
|
1200
|
-
} else {
|
|
1201
|
-
Platform.runLater(() -> showErrorDialog(throwable));
|
|
1202
|
-
}
|
|
1203
|
-
}
|
|
1204
|
-
|
|
1205
|
-
private void showErrorDialog(Throwable throwable) {
|
|
1206
|
-
AlertHelper.showException(
|
|
1207
|
-
"予期しないエラー",
|
|
1208
|
-
"アプリケーションで予期しないエラーが発生しました",
|
|
1209
|
-
throwable
|
|
1210
|
-
);
|
|
1211
|
-
}
|
|
1212
|
-
}
|
|
1213
|
-
```
|
|
1214
|
-
|
|
1215
|
-
</details>
|
|
1216
|
-
|
|
1217
|
-
### 17.3 ビジネス例外クラス
|
|
1218
|
-
|
|
1219
|
-
<details>
|
|
1220
|
-
<summary>コード例: ビジネス例外クラス</summary>
|
|
1221
|
-
|
|
1222
|
-
```java
|
|
1223
|
-
public class BusinessException extends RuntimeException {
|
|
1224
|
-
private final String errorCode;
|
|
1225
|
-
|
|
1226
|
-
public BusinessException(String errorCode, String message) {
|
|
1227
|
-
super(message);
|
|
1228
|
-
this.errorCode = errorCode;
|
|
1229
|
-
}
|
|
1230
|
-
|
|
1231
|
-
public String getErrorCode() { return errorCode; }
|
|
1232
|
-
}
|
|
1233
|
-
|
|
1234
|
-
public class EntityNotFoundException extends BusinessException {
|
|
1235
|
-
public EntityNotFoundException(String entityName, Object id) {
|
|
1236
|
-
super("NOT_FOUND", String.format("%s が見つかりません: %s", entityName, id));
|
|
1237
|
-
}
|
|
1238
|
-
}
|
|
1239
|
-
|
|
1240
|
-
public class ValidationException extends BusinessException {
|
|
1241
|
-
private final List<String> errors;
|
|
1242
|
-
|
|
1243
|
-
public ValidationException(List<String> errors) {
|
|
1244
|
-
super("VALIDATION_ERROR", String.join(", ", errors));
|
|
1245
|
-
this.errors = errors;
|
|
1246
|
-
}
|
|
1247
|
-
|
|
1248
|
-
public List<String> getErrors() { return errors; }
|
|
1249
|
-
}
|
|
1250
|
-
```
|
|
1251
|
-
|
|
1252
|
-
</details>
|
|
1253
|
-
|
|
1254
|
-
---
|
|
1255
|
-
|
|
1256
|
-
## Part 10-C のまとめ
|
|
1257
|
-
|
|
1258
|
-
### 実装した機能一覧
|
|
1259
|
-
|
|
1260
|
-
| 章 | 内容 |
|
|
1261
|
-
|----|------|
|
|
1262
|
-
| **第14章: 基礎** | JavaFX + Spring Boot 統合、MVVM パターン、Property と DataBinding |
|
|
1263
|
-
| **第15章: マスタ画面** | 商品・取引先マスタ CRUD、TestFX による TDD、Excel エクスポート |
|
|
1264
|
-
| **第16章: トランザクション画面** | 受注・出荷・請求処理、ヘッダ・明細構造、JasperReports 帳票出力 |
|
|
1265
|
-
| **第17章: エラーハンドリング** | 統一的なアラート表示、バリデーション、ロギング |
|
|
1266
|
-
|
|
1267
|
-
### アーキテクチャの特徴
|
|
1268
|
-
|
|
1269
|
-
```plantuml
|
|
1270
|
-
@startuml javafx_architecture
|
|
1271
|
-
|
|
1272
|
-
skinparam backgroundColor #FEFEFE
|
|
1273
|
-
skinparam packageStyle rectangle
|
|
1274
|
-
|
|
1275
|
-
package "UI Layer" {
|
|
1276
|
-
package "JavaFX UI" as javafx {
|
|
1277
|
-
[FXML/CSS] as fxml
|
|
1278
|
-
}
|
|
1279
|
-
package "REST API UI" as rest {
|
|
1280
|
-
[Controller] as controller
|
|
1281
|
-
}
|
|
1282
|
-
|
|
1283
|
-
[ViewModel] as viewmodel
|
|
1284
|
-
[DTO/Response] as dto
|
|
1285
|
-
|
|
1286
|
-
fxml --> viewmodel
|
|
1287
|
-
controller --> dto
|
|
1288
|
-
}
|
|
1289
|
-
|
|
1290
|
-
package "Application Layer" {
|
|
1291
|
-
[UseCase\n(ProductUseCase, OrderUseCase, etc.)] as usecase
|
|
1292
|
-
}
|
|
1293
|
-
|
|
1294
|
-
package "Domain Layer" {
|
|
1295
|
-
[Entity\n(Product)] as entity
|
|
1296
|
-
[Value Object] as vo
|
|
1297
|
-
[Repository\n(Port)] as repo_port
|
|
1298
|
-
}
|
|
1299
|
-
|
|
1300
|
-
package "Infrastructure Layer" {
|
|
1301
|
-
[MyBatis Mapper\n(Repository Implementation)] as mapper
|
|
1302
|
-
}
|
|
1303
|
-
|
|
1304
|
-
viewmodel --> usecase
|
|
1305
|
-
dto --> usecase
|
|
1306
|
-
usecase --> entity
|
|
1307
|
-
usecase --> vo
|
|
1308
|
-
usecase --> repo_port
|
|
1309
|
-
repo_port <|.. mapper : implements
|
|
1310
|
-
|
|
1311
|
-
@enduml
|
|
1312
|
-
```
|
|
1313
|
-
|
|
1314
|
-
### 技術スタック
|
|
1315
|
-
|
|
1316
|
-
| カテゴリ | 技術 |
|
|
1317
|
-
|---------|------|
|
|
1318
|
-
| **言語** | Java 21 |
|
|
1319
|
-
| **フレームワーク** | Spring Boot 3.4, JavaFX 21 |
|
|
1320
|
-
| **ORM** | MyBatis 3.0 |
|
|
1321
|
-
| **データベース** | PostgreSQL 16 |
|
|
1322
|
-
| **帳票** | JasperReports |
|
|
1323
|
-
| **Excel** | Apache POI |
|
|
1324
|
-
| **テスト** | JUnit 5, TestFX |
|
|
1325
|
-
|
|
1326
|
-
### API サーバー版との比較
|
|
1327
|
-
|
|
1328
|
-
| 観点 | API サーバー版 | デスクトップ版 |
|
|
1329
|
-
|------|--------------|---------------|
|
|
1330
|
-
| **UI** | React/Vue | JavaFX + FXML |
|
|
1331
|
-
| **通信** | HTTP/REST | 直接呼び出し |
|
|
1332
|
-
| **状態管理** | Redux/Vuex | JavaFX Property |
|
|
1333
|
-
| **配置** | サーバー + クライアント | スタンドアロン |
|
|
1334
|
-
| **オフライン** | 不可 | 可能 |
|
|
1335
|
-
| **帳票** | サーバー生成 PDF | クライアント直接印刷 |
|
|
1336
|
-
|
|
1337
|
-
### デスクトップアプリケーションの選択基準
|
|
1338
|
-
|
|
1339
|
-
| 選択基準 | デスクトップ向き | Web 向き |
|
|
1340
|
-
|---------|----------------|---------|
|
|
1341
|
-
| オフライン要件 | 必須 | 不要 |
|
|
1342
|
-
| 帳票・印刷 | 高度な制御 | 基本的な印刷 |
|
|
1343
|
-
| レスポンス | 即座の応答 | ネットワーク許容 |
|
|
1344
|
-
| 配布・更新 | 限定的 | 全ユーザー同時 |
|
|
1345
|
-
| UI 自由度 | 高い | HTML/CSS 制約 |
|
|
1346
|
-
|
|
1347
|
-
JavaFX デスクトップアプリケーションは、オフライン環境での利用や高度な帳票出力が必要な業務システムに適しています。既存のヘキサゴナルアーキテクチャを活かしながら、Input Adapter として追加することで、REST API と並行運用も可能です。
|
|
1
|
+
# 実践データベース設計:販売管理システム 研究 2 - JavaFX デスクトップアプリケーションの実装
|
|
2
|
+
|
|
3
|
+
## はじめに
|
|
4
|
+
|
|
5
|
+
本研究では、API サーバー構成(第13章)やモノリスサービス(研究 1)とは異なるアプローチとして、**JavaFX** によるデスクトップアプリケーションを実装します。FXML による宣言的 UI 定義と、データバインディングによるリアクティブな画面更新を活用した、業務アプリケーションを構築します。
|
|
6
|
+
|
|
7
|
+
研究 1 で構築したヘキサゴナルアーキテクチャ(ドメイン層・アプリケーション層)はそのまま共有し、**Input Adapter として JavaFX の Controller/ViewModel 層のみを追加**します。
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 第14章:JavaFX デスクトップアプリケーションの基礎
|
|
12
|
+
|
|
13
|
+
### 14.1 JavaFX とは
|
|
14
|
+
|
|
15
|
+
JavaFX は Java のリッチクライアントプラットフォームです。Swing の後継として位置づけられ、モダンな UI 構築が可能です。
|
|
16
|
+
|
|
17
|
+
```plantuml
|
|
18
|
+
@startuml javafx_architecture
|
|
19
|
+
!define RECTANGLE class
|
|
20
|
+
|
|
21
|
+
skinparam backgroundColor #FEFEFE
|
|
22
|
+
|
|
23
|
+
package "JavaFX Architecture (販売管理システム)" {
|
|
24
|
+
|
|
25
|
+
package "View Layer" {
|
|
26
|
+
RECTANGLE "FXML Files\n(宣言的 UI 定義)" as fxml {
|
|
27
|
+
- MainView.fxml
|
|
28
|
+
- ProductView.fxml
|
|
29
|
+
- OrderView.fxml
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
RECTANGLE "CSS Stylesheets\n(スタイル定義)" as css {
|
|
33
|
+
- application.css
|
|
34
|
+
- theme-light.css
|
|
35
|
+
- theme-dark.css
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
package "Controller/ViewModel Layer" {
|
|
40
|
+
RECTANGLE "Controllers\n(画面制御)" as controller {
|
|
41
|
+
- MainController
|
|
42
|
+
- ProductController
|
|
43
|
+
- OrderController
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
RECTANGLE "ViewModels\n(画面状態管理)" as viewmodel {
|
|
47
|
+
- ProductViewModel
|
|
48
|
+
- OrderViewModel
|
|
49
|
+
- CustomerViewModel
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
package "Application Core" {
|
|
54
|
+
RECTANGLE "Use Cases\n(ビジネスロジック)" as usecase {
|
|
55
|
+
- ProductUseCase
|
|
56
|
+
- OrderUseCase
|
|
57
|
+
- InvoiceUseCase
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
fxml --> controller : "fx:controller"
|
|
63
|
+
css --> fxml : "stylesheets"
|
|
64
|
+
controller --> viewmodel : "データバインド"
|
|
65
|
+
viewmodel --> usecase : "Input Port"
|
|
66
|
+
|
|
67
|
+
note bottom of fxml
|
|
68
|
+
Scene Builder で
|
|
69
|
+
視覚的に編集可能
|
|
70
|
+
end note
|
|
71
|
+
|
|
72
|
+
note right of viewmodel
|
|
73
|
+
ObservableList,
|
|
74
|
+
Property による
|
|
75
|
+
双方向バインディング
|
|
76
|
+
end note
|
|
77
|
+
|
|
78
|
+
@enduml
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**Web アプリケーションとの比較:**
|
|
82
|
+
|
|
83
|
+
| 特徴 | Web アプリ(REST API) | JavaFX デスクトップ |
|
|
84
|
+
|------|----------------------|-------------------|
|
|
85
|
+
| 配置 | サーバー + ブラウザ | ローカルインストール |
|
|
86
|
+
| オフライン | 不可 | 可能(ローカル DB) |
|
|
87
|
+
| レスポンス | ネットワーク依存 | 高速 |
|
|
88
|
+
| UI 自由度 | HTML/CSS 制約 | ネイティブ制御 |
|
|
89
|
+
| 印刷機能 | 制限あり | 高機能 |
|
|
90
|
+
| 更新配布 | 自動 | 手動/自動更新 |
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
### 14.2 MVVM パターンの採用
|
|
95
|
+
|
|
96
|
+
JavaFX では、**MVVM(Model-View-ViewModel)** パターンを採用し、UI とビジネスロジックを分離します。
|
|
97
|
+
|
|
98
|
+
```plantuml
|
|
99
|
+
@startuml mvvm_pattern
|
|
100
|
+
!define RECTANGLE class
|
|
101
|
+
|
|
102
|
+
skinparam backgroundColor #FEFEFE
|
|
103
|
+
|
|
104
|
+
package "MVVM Pattern" {
|
|
105
|
+
|
|
106
|
+
RECTANGLE "View\n(FXML + Controller)" as view {
|
|
107
|
+
+ TableView<ProductRow>
|
|
108
|
+
+ TextField
|
|
109
|
+
+ Button
|
|
110
|
+
--
|
|
111
|
+
initialize()
|
|
112
|
+
onSaveClick()
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
RECTANGLE "ViewModel\n(画面状態)" as vm {
|
|
116
|
+
+ ObservableList<ProductRow> products
|
|
117
|
+
+ StringProperty searchKeyword
|
|
118
|
+
+ BooleanProperty loading
|
|
119
|
+
--
|
|
120
|
+
search()
|
|
121
|
+
save()
|
|
122
|
+
delete()
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
RECTANGLE "Model\n(Domain + UseCase)" as model {
|
|
126
|
+
+ Product
|
|
127
|
+
+ ProductUseCase
|
|
128
|
+
+ ProductRepository
|
|
129
|
+
--
|
|
130
|
+
findByCode()
|
|
131
|
+
save()
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
view --> vm : "Data Binding\n(双方向)"
|
|
136
|
+
vm --> model : "UseCase 呼び出し"
|
|
137
|
+
model --> vm : "結果を Property に反映"
|
|
138
|
+
vm --> view : "自動 UI 更新"
|
|
139
|
+
|
|
140
|
+
note right of view
|
|
141
|
+
FXML で宣言的に定義
|
|
142
|
+
Controller で
|
|
143
|
+
イベントハンドリング
|
|
144
|
+
end note
|
|
145
|
+
|
|
146
|
+
note right of vm
|
|
147
|
+
JavaFX Property で
|
|
148
|
+
リアクティブな
|
|
149
|
+
状態管理
|
|
150
|
+
end note
|
|
151
|
+
|
|
152
|
+
note right of model
|
|
153
|
+
API版と共有
|
|
154
|
+
変更不要
|
|
155
|
+
end note
|
|
156
|
+
|
|
157
|
+
@enduml
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**各層の責務:**
|
|
161
|
+
|
|
162
|
+
| 層 | 責務 | JavaFX での実装 |
|
|
163
|
+
|----|------|----------------|
|
|
164
|
+
| **View** | UI 表示・ユーザー入力 | FXML + Controller |
|
|
165
|
+
| **ViewModel** | 画面状態管理・変換 | Property, ObservableList |
|
|
166
|
+
| **Model** | ビジネスロジック | UseCase, Repository |
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
### 14.3 ヘキサゴナルアーキテクチャとの統合
|
|
171
|
+
|
|
172
|
+
JavaFX を導入しても、既存のヘキサゴナルアーキテクチャ(ドメイン層・アプリケーション層)はそのまま共有し、**Input Adapter として JavaFX の Controller/ViewModel 層のみを追加**します。
|
|
173
|
+
|
|
174
|
+
```plantuml
|
|
175
|
+
@startuml hexagonal_javafx
|
|
176
|
+
!define RECTANGLE class
|
|
177
|
+
|
|
178
|
+
package "Hexagonal Architecture (JavaFX版)" {
|
|
179
|
+
|
|
180
|
+
RECTANGLE "Application Core\n(Domain + Use Cases)" as core {
|
|
181
|
+
- Product (商品)
|
|
182
|
+
- Partner (取引先)
|
|
183
|
+
- Order (受注)
|
|
184
|
+
- Shipment (出荷)
|
|
185
|
+
- ProductUseCase
|
|
186
|
+
- OrderUseCase
|
|
187
|
+
- ShipmentUseCase
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
RECTANGLE "Input Adapters\n(Driving Side)" as input {
|
|
191
|
+
- REST Controller(API版)
|
|
192
|
+
- JavaFX Controller(新規追加)
|
|
193
|
+
- ViewModel
|
|
194
|
+
- FXML View
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
RECTANGLE "Output Adapters\n(Driven Side)" as output {
|
|
198
|
+
- MyBatis Repository
|
|
199
|
+
- Database Access
|
|
200
|
+
- Entity Mapping
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
input --> core : "Input Ports\n(Use Cases)"
|
|
205
|
+
core --> output : "Output Ports\n(Repository Interfaces)"
|
|
206
|
+
|
|
207
|
+
note top of core
|
|
208
|
+
既存のビジネスロジック
|
|
209
|
+
REST API 版と完全に共有
|
|
210
|
+
JavaFX 固有のコードは含まない
|
|
211
|
+
end note
|
|
212
|
+
|
|
213
|
+
note left of input
|
|
214
|
+
JavaFX Controller を
|
|
215
|
+
Input Adapter として追加
|
|
216
|
+
既存の REST API と共存可能
|
|
217
|
+
end note
|
|
218
|
+
|
|
219
|
+
note right of output
|
|
220
|
+
既存の Repository を
|
|
221
|
+
そのまま使用
|
|
222
|
+
変更不要
|
|
223
|
+
end note
|
|
224
|
+
|
|
225
|
+
@enduml
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
**JavaFX でもヘキサゴナルアーキテクチャを維持する理由:**
|
|
229
|
+
|
|
230
|
+
1. **再利用性**: 既存の UseCase/Repository をそのまま活用
|
|
231
|
+
2. **並行運用**: REST API と JavaFX の両方を提供可能
|
|
232
|
+
3. **テスト容易性**: ドメインロジックは UI フレームワークに依存しない
|
|
233
|
+
4. **移行容易性**: 段階的に Web からデスクトップへ移行可能
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
### 14.4 ディレクトリ構成
|
|
238
|
+
|
|
239
|
+
既存の構成に `infrastructure/in/javafx/` を追加します。
|
|
240
|
+
|
|
241
|
+
```
|
|
242
|
+
apps/sms/backend/src/main/java/com/example/sms/
|
|
243
|
+
├── domain/ # ドメイン層(API版と共通)
|
|
244
|
+
│ ├── model/
|
|
245
|
+
│ │ ├── product/
|
|
246
|
+
│ │ ├── partner/
|
|
247
|
+
│ │ ├── sales/
|
|
248
|
+
│ │ └── invoice/
|
|
249
|
+
│ └── exception/
|
|
250
|
+
│
|
|
251
|
+
├── application/ # アプリケーション層(API版と共通)
|
|
252
|
+
│ ├── port/
|
|
253
|
+
│ │ ├── in/ # Input Port(ユースケース)
|
|
254
|
+
│ │ └── out/ # Output Port(リポジトリ)
|
|
255
|
+
│ └── service/
|
|
256
|
+
│
|
|
257
|
+
├── infrastructure/
|
|
258
|
+
│ ├── out/
|
|
259
|
+
│ │ └── persistence/ # Output Adapter(DB実装)- 既存
|
|
260
|
+
│ │ ├── mapper/
|
|
261
|
+
│ │ └── repository/
|
|
262
|
+
│ ├── in/
|
|
263
|
+
│ │ ├── rest/ # Input Adapter(REST実装)- 既存
|
|
264
|
+
│ │ │ ├── controller/
|
|
265
|
+
│ │ │ └── dto/
|
|
266
|
+
│ │ └── javafx/ # Input Adapter(JavaFX実装)- 新規追加
|
|
267
|
+
│ │ ├── controller/ # FXML Controller
|
|
268
|
+
│ │ ├── viewmodel/ # ViewModel
|
|
269
|
+
│ │ ├── view/ # 画面表示用の行モデル
|
|
270
|
+
│ │ ├── dialog/ # ダイアログ
|
|
271
|
+
│ │ ├── util/ # ユーティリティ
|
|
272
|
+
│ │ └── config/ # JavaFX 設定
|
|
273
|
+
│
|
|
274
|
+
└── Application.java # Spring Boot + JavaFX 起動クラス
|
|
275
|
+
|
|
276
|
+
apps/sms/backend/src/main/resources/
|
|
277
|
+
├── fxml/ # FXML ファイル
|
|
278
|
+
│ ├── main.fxml
|
|
279
|
+
│ ├── product/
|
|
280
|
+
│ │ ├── product-list.fxml
|
|
281
|
+
│ │ └── product-edit.fxml
|
|
282
|
+
│ ├── order/
|
|
283
|
+
│ │ ├── order-list.fxml
|
|
284
|
+
│ │ └── order-entry.fxml
|
|
285
|
+
│ └── common/
|
|
286
|
+
│ ├── login.fxml
|
|
287
|
+
│ └── error-dialog.fxml
|
|
288
|
+
│
|
|
289
|
+
├── css/ # スタイルシート
|
|
290
|
+
│ ├── application.css
|
|
291
|
+
│ └── theme/
|
|
292
|
+
│
|
|
293
|
+
├── images/ # 画像リソース
|
|
294
|
+
│
|
|
295
|
+
└── reports/ # JasperReports テンプレート
|
|
296
|
+
├── invoice.jrxml
|
|
297
|
+
└── delivery-slip.jrxml
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
### 14.5 技術スタックの追加
|
|
303
|
+
|
|
304
|
+
#### build.gradle.kts(差分)
|
|
305
|
+
|
|
306
|
+
<details>
|
|
307
|
+
<summary>コード例: build.gradle.kts</summary>
|
|
308
|
+
|
|
309
|
+
```kotlin
|
|
310
|
+
plugins {
|
|
311
|
+
// 既存のプラグイン...
|
|
312
|
+
id("org.openjfx.javafxplugin") version "0.1.0" // 追加
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// JavaFX 設定
|
|
316
|
+
javafx {
|
|
317
|
+
version = "21"
|
|
318
|
+
modules = listOf(
|
|
319
|
+
"javafx.controls",
|
|
320
|
+
"javafx.fxml",
|
|
321
|
+
"javafx.graphics",
|
|
322
|
+
"javafx.swing" // JasperReports 連携用
|
|
323
|
+
)
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
dependencies {
|
|
327
|
+
// 既存の依存関係(Spring Boot, MyBatis, PostgreSQL等)はそのまま
|
|
328
|
+
|
|
329
|
+
// JavaFX 関連を追加
|
|
330
|
+
implementation("org.controlsfx:controlsfx:11.2.0") // 拡張コントロール
|
|
331
|
+
implementation("com.dlsc.formsfx:formsfx-core:11.6.0") // フォーム生成
|
|
332
|
+
implementation("org.kordamp.ikonli:ikonli-javafx:12.3.1") // アイコン
|
|
333
|
+
implementation("org.kordamp.ikonli:ikonli-fontawesome5-pack:12.3.1")
|
|
334
|
+
|
|
335
|
+
// JasperReports(帳票出力)
|
|
336
|
+
implementation("net.sf.jasperreports:jasperreports:6.20.6")
|
|
337
|
+
|
|
338
|
+
// Excel 出力
|
|
339
|
+
implementation("org.apache.poi:poi-ooxml:5.2.5")
|
|
340
|
+
|
|
341
|
+
// TestFX(UI テスト)
|
|
342
|
+
testImplementation("org.testfx:testfx-core:4.0.18")
|
|
343
|
+
testImplementation("org.testfx:testfx-junit5:4.0.18")
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
</details>
|
|
348
|
+
|
|
349
|
+
**追加パッケージの説明:**
|
|
350
|
+
|
|
351
|
+
| パッケージ | 用途 |
|
|
352
|
+
|-----------|------|
|
|
353
|
+
| javafx.controls | 標準 UI コントロール |
|
|
354
|
+
| javafx.fxml | FXML パーサー |
|
|
355
|
+
| ControlsFX | 拡張コントロール(SearchableComboBox等) |
|
|
356
|
+
| Ikonli | アイコンフォント |
|
|
357
|
+
| JasperReports | 帳票出力 |
|
|
358
|
+
| Apache POI | Excel 出力 |
|
|
359
|
+
| TestFX | UI 自動テスト |
|
|
360
|
+
|
|
361
|
+
---
|
|
362
|
+
|
|
363
|
+
### 14.6 Spring Boot + JavaFX 統合
|
|
364
|
+
|
|
365
|
+
#### JavaFxApplication.java
|
|
366
|
+
|
|
367
|
+
<details>
|
|
368
|
+
<summary>コード例: JavaFxApplication.java</summary>
|
|
369
|
+
|
|
370
|
+
```java
|
|
371
|
+
package com.example.sms.infrastructure.in.javafx;
|
|
372
|
+
|
|
373
|
+
import javafx.application.Application;
|
|
374
|
+
import javafx.application.Platform;
|
|
375
|
+
import javafx.stage.Stage;
|
|
376
|
+
import org.springframework.boot.builder.SpringApplicationBuilder;
|
|
377
|
+
import org.springframework.context.ConfigurableApplicationContext;
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* JavaFX アプリケーションのエントリーポイント
|
|
381
|
+
* Spring Boot コンテキストと JavaFX を統合
|
|
382
|
+
*/
|
|
383
|
+
public class JavaFxApplication extends Application {
|
|
384
|
+
|
|
385
|
+
private ConfigurableApplicationContext springContext;
|
|
386
|
+
|
|
387
|
+
@Override
|
|
388
|
+
public void init() {
|
|
389
|
+
springContext = new SpringApplicationBuilder(SmsApplication.class).run();
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
@Override
|
|
393
|
+
public void start(Stage primaryStage) {
|
|
394
|
+
StageManager stageManager = springContext.getBean(StageManager.class);
|
|
395
|
+
stageManager.setPrimaryStage(primaryStage);
|
|
396
|
+
stageManager.switchScene(FxmlView.MAIN);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
@Override
|
|
400
|
+
public void stop() {
|
|
401
|
+
springContext.close();
|
|
402
|
+
Platform.exit();
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
</details>
|
|
408
|
+
|
|
409
|
+
#### StageManager.java
|
|
410
|
+
|
|
411
|
+
<details>
|
|
412
|
+
<summary>コード例: StageManager.java</summary>
|
|
413
|
+
|
|
414
|
+
```java
|
|
415
|
+
package com.example.sms.infrastructure.in.javafx.config;
|
|
416
|
+
|
|
417
|
+
import javafx.fxml.FXMLLoader;
|
|
418
|
+
import javafx.scene.Parent;
|
|
419
|
+
import javafx.scene.Scene;
|
|
420
|
+
import javafx.stage.Stage;
|
|
421
|
+
import org.springframework.context.ApplicationContext;
|
|
422
|
+
import org.springframework.stereotype.Component;
|
|
423
|
+
|
|
424
|
+
import java.io.IOException;
|
|
425
|
+
import java.util.Objects;
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* JavaFX Stage/Scene の管理
|
|
429
|
+
*/
|
|
430
|
+
@Component
|
|
431
|
+
public class StageManager {
|
|
432
|
+
|
|
433
|
+
private final ApplicationContext applicationContext;
|
|
434
|
+
private Stage primaryStage;
|
|
435
|
+
|
|
436
|
+
public StageManager(ApplicationContext applicationContext) {
|
|
437
|
+
this.applicationContext = applicationContext;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
public void setPrimaryStage(Stage primaryStage) {
|
|
441
|
+
this.primaryStage = primaryStage;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
public void switchScene(FxmlView view) {
|
|
445
|
+
Parent root = loadView(view.getFxmlPath());
|
|
446
|
+
Scene scene = new Scene(root);
|
|
447
|
+
|
|
448
|
+
scene.getStylesheets().add(
|
|
449
|
+
Objects.requireNonNull(
|
|
450
|
+
getClass().getResource("/css/application.css")
|
|
451
|
+
).toExternalForm()
|
|
452
|
+
);
|
|
453
|
+
|
|
454
|
+
primaryStage.setTitle(view.getTitle());
|
|
455
|
+
primaryStage.setScene(scene);
|
|
456
|
+
primaryStage.centerOnScreen();
|
|
457
|
+
|
|
458
|
+
if (!primaryStage.isShowing()) {
|
|
459
|
+
primaryStage.show();
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
private Parent loadView(String fxmlPath) {
|
|
464
|
+
try {
|
|
465
|
+
FXMLLoader loader = new FXMLLoader(getClass().getResource(fxmlPath));
|
|
466
|
+
loader.setControllerFactory(applicationContext::getBean);
|
|
467
|
+
return loader.load();
|
|
468
|
+
} catch (IOException e) {
|
|
469
|
+
throw new RuntimeException("FXML ロードエラー: " + fxmlPath, e);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
public ApplicationContext getApplicationContext() {
|
|
474
|
+
return applicationContext;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
</details>
|
|
480
|
+
|
|
481
|
+
#### FxmlView.java(画面定義)
|
|
482
|
+
|
|
483
|
+
<details>
|
|
484
|
+
<summary>コード例: FxmlView.java</summary>
|
|
485
|
+
|
|
486
|
+
```java
|
|
487
|
+
package com.example.sms.infrastructure.in.javafx.config;
|
|
488
|
+
|
|
489
|
+
public enum FxmlView {
|
|
490
|
+
|
|
491
|
+
MAIN("/fxml/main.fxml", "販売管理システム"),
|
|
492
|
+
PRODUCT_LIST("/fxml/product/product-list.fxml", "商品マスタ"),
|
|
493
|
+
ORDER_ENTRY("/fxml/order/order-entry.fxml", "受注入力"),
|
|
494
|
+
ORDER_LIST("/fxml/order/order-list.fxml", "受注一覧"),
|
|
495
|
+
SHIPMENT_LIST("/fxml/shipment/shipment-list.fxml", "出荷一覧"),
|
|
496
|
+
INVOICE_LIST("/fxml/invoice/invoice-list.fxml", "請求一覧");
|
|
497
|
+
|
|
498
|
+
private final String fxmlPath;
|
|
499
|
+
private final String title;
|
|
500
|
+
|
|
501
|
+
FxmlView(String fxmlPath, String title) {
|
|
502
|
+
this.fxmlPath = fxmlPath;
|
|
503
|
+
this.title = title;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
public String getFxmlPath() { return fxmlPath; }
|
|
507
|
+
public String getTitle() { return title; }
|
|
508
|
+
}
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
</details>
|
|
512
|
+
|
|
513
|
+
---
|
|
514
|
+
|
|
515
|
+
### 14.7 JavaFX Property の活用
|
|
516
|
+
|
|
517
|
+
JavaFX の Property 機能を使って、UI とデータの双方向バインディングを実現します。
|
|
518
|
+
|
|
519
|
+
#### ProductRow.java(View 用データクラス)
|
|
520
|
+
|
|
521
|
+
<details>
|
|
522
|
+
<summary>コード例: ProductRow.java</summary>
|
|
523
|
+
|
|
524
|
+
```java
|
|
525
|
+
package com.example.sms.infrastructure.in.javafx.view;
|
|
526
|
+
|
|
527
|
+
import javafx.beans.property.*;
|
|
528
|
+
import java.math.BigDecimal;
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* 商品マスタ画面用の行モデル
|
|
532
|
+
*/
|
|
533
|
+
public class ProductRow {
|
|
534
|
+
|
|
535
|
+
private final StringProperty productCode = new SimpleStringProperty();
|
|
536
|
+
private final StringProperty productName = new SimpleStringProperty();
|
|
537
|
+
private final StringProperty categoryName = new SimpleStringProperty();
|
|
538
|
+
private final ObjectProperty<BigDecimal> unitPrice = new SimpleObjectProperty<>();
|
|
539
|
+
private final BooleanProperty discontinued = new SimpleBooleanProperty();
|
|
540
|
+
|
|
541
|
+
// Property アクセサ
|
|
542
|
+
public StringProperty productCodeProperty() { return productCode; }
|
|
543
|
+
public StringProperty productNameProperty() { return productName; }
|
|
544
|
+
public StringProperty categoryNameProperty() { return categoryName; }
|
|
545
|
+
public ObjectProperty<BigDecimal> unitPriceProperty() { return unitPrice; }
|
|
546
|
+
public BooleanProperty discontinuedProperty() { return discontinued; }
|
|
547
|
+
|
|
548
|
+
// Getter/Setter
|
|
549
|
+
public String getProductCode() { return productCode.get(); }
|
|
550
|
+
public void setProductCode(String value) { productCode.set(value); }
|
|
551
|
+
|
|
552
|
+
public String getProductName() { return productName.get(); }
|
|
553
|
+
public void setProductName(String value) { productName.set(value); }
|
|
554
|
+
|
|
555
|
+
public BigDecimal getUnitPrice() { return unitPrice.get(); }
|
|
556
|
+
public void setUnitPrice(BigDecimal value) { unitPrice.set(value); }
|
|
557
|
+
}
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
</details>
|
|
561
|
+
|
|
562
|
+
#### ProductViewModel.java
|
|
563
|
+
|
|
564
|
+
<details>
|
|
565
|
+
<summary>コード例: ProductViewModel.java</summary>
|
|
566
|
+
|
|
567
|
+
```java
|
|
568
|
+
package com.example.sms.infrastructure.in.javafx.viewmodel;
|
|
569
|
+
|
|
570
|
+
import com.example.sms.application.port.in.ProductUseCase;
|
|
571
|
+
import com.example.sms.domain.model.product.Product;
|
|
572
|
+
import com.example.sms.infrastructure.in.javafx.view.ProductRow;
|
|
573
|
+
import javafx.beans.property.*;
|
|
574
|
+
import javafx.collections.FXCollections;
|
|
575
|
+
import javafx.collections.ObservableList;
|
|
576
|
+
import org.springframework.stereotype.Component;
|
|
577
|
+
|
|
578
|
+
import java.util.List;
|
|
579
|
+
|
|
580
|
+
@Component
|
|
581
|
+
public class ProductViewModel {
|
|
582
|
+
|
|
583
|
+
private final ProductUseCase productUseCase;
|
|
584
|
+
|
|
585
|
+
private final ObservableList<ProductRow> products = FXCollections.observableArrayList();
|
|
586
|
+
private final ObjectProperty<ProductRow> selectedProduct = new SimpleObjectProperty<>();
|
|
587
|
+
private final StringProperty searchKeyword = new SimpleStringProperty("");
|
|
588
|
+
private final BooleanProperty loading = new SimpleBooleanProperty(false);
|
|
589
|
+
private final StringProperty errorMessage = new SimpleStringProperty();
|
|
590
|
+
|
|
591
|
+
public ProductViewModel(ProductUseCase productUseCase) {
|
|
592
|
+
this.productUseCase = productUseCase;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
public void search() {
|
|
596
|
+
loading.set(true);
|
|
597
|
+
errorMessage.set(null);
|
|
598
|
+
|
|
599
|
+
try {
|
|
600
|
+
List<Product> result = searchKeyword.get().isBlank()
|
|
601
|
+
? productUseCase.findAll()
|
|
602
|
+
: productUseCase.searchByKeyword(searchKeyword.get());
|
|
603
|
+
|
|
604
|
+
products.clear();
|
|
605
|
+
result.forEach(p -> products.add(toRow(p)));
|
|
606
|
+
} catch (Exception e) {
|
|
607
|
+
errorMessage.set("検索エラー: " + e.getMessage());
|
|
608
|
+
} finally {
|
|
609
|
+
loading.set(false);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
public void delete(String productCode) {
|
|
614
|
+
loading.set(true);
|
|
615
|
+
try {
|
|
616
|
+
productUseCase.delete(productCode);
|
|
617
|
+
products.removeIf(row -> row.getProductCode().equals(productCode));
|
|
618
|
+
} catch (Exception e) {
|
|
619
|
+
errorMessage.set("削除エラー: " + e.getMessage());
|
|
620
|
+
throw e;
|
|
621
|
+
} finally {
|
|
622
|
+
loading.set(false);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
public ObservableList<ProductRow> getProducts() { return products; }
|
|
627
|
+
public StringProperty searchKeywordProperty() { return searchKeyword; }
|
|
628
|
+
public BooleanProperty loadingProperty() { return loading; }
|
|
629
|
+
|
|
630
|
+
private ProductRow toRow(Product product) {
|
|
631
|
+
ProductRow row = new ProductRow();
|
|
632
|
+
row.setProductCode(product.getProductCode());
|
|
633
|
+
row.setProductName(product.getProductName());
|
|
634
|
+
row.setUnitPrice(product.getUnitPrice());
|
|
635
|
+
return row;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
</details>
|
|
641
|
+
|
|
642
|
+
---
|
|
643
|
+
|
|
644
|
+
## 第15章:マスタの実装
|
|
645
|
+
|
|
646
|
+
### 15.1 商品マスタ画面の設計
|
|
647
|
+
|
|
648
|
+
```plantuml
|
|
649
|
+
@startuml product_master_screen
|
|
650
|
+
|
|
651
|
+
package "商品マスタ画面構成" {
|
|
652
|
+
|
|
653
|
+
class "ProductListController\n(一覧画面)" as list {
|
|
654
|
+
+ TableView<ProductRow>
|
|
655
|
+
+ TextField (検索)
|
|
656
|
+
+ Button (新規/編集/削除)
|
|
657
|
+
--
|
|
658
|
+
initialize()
|
|
659
|
+
onSearch()
|
|
660
|
+
onNew()
|
|
661
|
+
onEdit()
|
|
662
|
+
onDelete()
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
class "ProductEditController\n(編集画面)" as edit {
|
|
666
|
+
+ TextField (コード/名称)
|
|
667
|
+
+ ComboBox (分類)
|
|
668
|
+
+ TextField (単価)
|
|
669
|
+
--
|
|
670
|
+
initialize()
|
|
671
|
+
onSave()
|
|
672
|
+
onCancel()
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
class "ProductViewModel\n(画面状態)" as vm {
|
|
676
|
+
+ ObservableList<ProductRow>
|
|
677
|
+
+ selectedProduct
|
|
678
|
+
+ searchKeyword
|
|
679
|
+
--
|
|
680
|
+
search()
|
|
681
|
+
create()
|
|
682
|
+
update()
|
|
683
|
+
delete()
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
class "ProductUseCase\n(ビジネスロジック)" as usecase {
|
|
687
|
+
+ findAll()
|
|
688
|
+
+ findByCode()
|
|
689
|
+
+ create()
|
|
690
|
+
+ update()
|
|
691
|
+
+ delete()
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
list --> vm : "バインド"
|
|
696
|
+
edit --> vm : "バインド"
|
|
697
|
+
vm --> usecase : "Input Port"
|
|
698
|
+
|
|
699
|
+
@enduml
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
### 15.2 商品マスタ一覧画面の TDD 実装
|
|
703
|
+
|
|
704
|
+
#### Red: 失敗するテストを書く
|
|
705
|
+
|
|
706
|
+
<details>
|
|
707
|
+
<summary>コード例: ProductListControllerTest.java</summary>
|
|
708
|
+
|
|
709
|
+
```java
|
|
710
|
+
@ExtendWith({ApplicationExtension.class, MockitoExtension.class})
|
|
711
|
+
@DisplayName("商品マスタ一覧画面")
|
|
712
|
+
class ProductListControllerTest {
|
|
713
|
+
|
|
714
|
+
@Mock
|
|
715
|
+
private ProductUseCase productUseCase;
|
|
716
|
+
|
|
717
|
+
private ProductViewModel viewModel;
|
|
718
|
+
|
|
719
|
+
@Start
|
|
720
|
+
void start(Stage stage) throws Exception {
|
|
721
|
+
viewModel = new ProductViewModel(productUseCase);
|
|
722
|
+
var controller = new ProductListController(viewModel);
|
|
723
|
+
|
|
724
|
+
var loader = new FXMLLoader(getClass().getResource("/fxml/product/product-list.fxml"));
|
|
725
|
+
loader.setController(controller);
|
|
726
|
+
stage.setScene(new Scene(loader.load()));
|
|
727
|
+
stage.show();
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
@Test
|
|
731
|
+
@DisplayName("商品一覧がテーブルに表示される")
|
|
732
|
+
void shouldDisplayProductsInTable(FxRobot robot) {
|
|
733
|
+
// Arrange
|
|
734
|
+
when(productUseCase.findAll()).thenReturn(List.of(
|
|
735
|
+
createProduct("P001", "テスト商品A", new BigDecimal("500")),
|
|
736
|
+
createProduct("P002", "テスト商品B", new BigDecimal("800"))
|
|
737
|
+
));
|
|
738
|
+
|
|
739
|
+
// Act
|
|
740
|
+
robot.interact(() -> viewModel.search());
|
|
741
|
+
|
|
742
|
+
// Assert
|
|
743
|
+
TableView<ProductRow> table = robot.lookup("#productTable").queryTableView();
|
|
744
|
+
assertThat(table.getItems()).hasSize(2);
|
|
745
|
+
assertThat(table.getItems().get(0).getProductCode()).isEqualTo("P001");
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
@Test
|
|
749
|
+
@DisplayName("キーワードで商品を検索できる")
|
|
750
|
+
void shouldSearchProductsByKeyword(FxRobot robot) {
|
|
751
|
+
when(productUseCase.searchByKeyword("テスト")).thenReturn(List.of(
|
|
752
|
+
createProduct("P001", "テスト商品A", new BigDecimal("500"))
|
|
753
|
+
));
|
|
754
|
+
|
|
755
|
+
robot.clickOn("#searchField").write("テスト").type(KeyCode.ENTER);
|
|
756
|
+
|
|
757
|
+
TableView<ProductRow> table = robot.lookup("#productTable").queryTableView();
|
|
758
|
+
assertThat(table.getItems()).hasSize(1);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
</details>
|
|
764
|
+
|
|
765
|
+
#### product-list.fxml
|
|
766
|
+
|
|
767
|
+
<details>
|
|
768
|
+
<summary>コード例: product-list.fxml</summary>
|
|
769
|
+
|
|
770
|
+
```xml
|
|
771
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
772
|
+
<?import javafx.scene.control.*?>
|
|
773
|
+
<?import javafx.scene.layout.*?>
|
|
774
|
+
<?import org.kordamp.ikonli.javafx.FontIcon?>
|
|
775
|
+
|
|
776
|
+
<VBox xmlns="http://javafx.com/javafx/21"
|
|
777
|
+
xmlns:fx="http://javafx.com/fxml/1"
|
|
778
|
+
fx:controller="com.example.sms.infrastructure.in.javafx.controller.ProductListController"
|
|
779
|
+
spacing="10">
|
|
780
|
+
|
|
781
|
+
<!-- ツールバー -->
|
|
782
|
+
<HBox spacing="10" alignment="CENTER_LEFT">
|
|
783
|
+
<TextField fx:id="searchField" promptText="商品コード・商品名で検索" prefWidth="300"/>
|
|
784
|
+
<Button onAction="#onSearch">
|
|
785
|
+
<graphic><FontIcon iconLiteral="fas-search"/></graphic>
|
|
786
|
+
</Button>
|
|
787
|
+
<Pane HBox.hgrow="ALWAYS"/>
|
|
788
|
+
<Button fx:id="newButton" text="新規" styleClass="primary" onAction="#onNew">
|
|
789
|
+
<graphic><FontIcon iconLiteral="fas-plus"/></graphic>
|
|
790
|
+
</Button>
|
|
791
|
+
<Button fx:id="editButton" text="編集" onAction="#onEdit" disable="true"/>
|
|
792
|
+
<Button fx:id="deleteButton" text="削除" styleClass="danger" onAction="#onDelete" disable="true"/>
|
|
793
|
+
<Button text="Excel出力" onAction="#onExportExcel">
|
|
794
|
+
<graphic><FontIcon iconLiteral="fas-file-excel"/></graphic>
|
|
795
|
+
</Button>
|
|
796
|
+
</HBox>
|
|
797
|
+
|
|
798
|
+
<!-- 商品テーブル -->
|
|
799
|
+
<TableView fx:id="productTable" VBox.vgrow="ALWAYS">
|
|
800
|
+
<placeholder><Label text="該当する商品がありません"/></placeholder>
|
|
801
|
+
<columns>
|
|
802
|
+
<TableColumn text="商品コード" prefWidth="100">
|
|
803
|
+
<cellValueFactory><PropertyValueFactory property="productCode"/></cellValueFactory>
|
|
804
|
+
</TableColumn>
|
|
805
|
+
<TableColumn text="商品名" prefWidth="200">
|
|
806
|
+
<cellValueFactory><PropertyValueFactory property="productName"/></cellValueFactory>
|
|
807
|
+
</TableColumn>
|
|
808
|
+
<TableColumn text="単価" prefWidth="100" styleClass="amount-column">
|
|
809
|
+
<cellValueFactory><PropertyValueFactory property="unitPrice"/></cellValueFactory>
|
|
810
|
+
</TableColumn>
|
|
811
|
+
</columns>
|
|
812
|
+
</TableView>
|
|
813
|
+
|
|
814
|
+
<!-- ステータスバー -->
|
|
815
|
+
<HBox spacing="10" styleClass="status-bar">
|
|
816
|
+
<Label fx:id="recordCount" text="0 件"/>
|
|
817
|
+
<Pane HBox.hgrow="ALWAYS"/>
|
|
818
|
+
<ProgressIndicator fx:id="loadingIndicator" visible="false" prefWidth="20" prefHeight="20"/>
|
|
819
|
+
</HBox>
|
|
820
|
+
</VBox>
|
|
821
|
+
```
|
|
822
|
+
|
|
823
|
+
</details>
|
|
824
|
+
|
|
825
|
+
---
|
|
826
|
+
|
|
827
|
+
## 第16章:トランザクションの実装
|
|
828
|
+
|
|
829
|
+
### 16.1 トランザクション画面の特徴
|
|
830
|
+
|
|
831
|
+
トランザクション画面は、マスタ画面とは異なる以下の特徴があります。
|
|
832
|
+
|
|
833
|
+
```plantuml
|
|
834
|
+
@startuml transaction_screen_pattern
|
|
835
|
+
|
|
836
|
+
package "トランザクション画面の特徴" {
|
|
837
|
+
|
|
838
|
+
class "ヘッダ・明細構造" as header_detail {
|
|
839
|
+
+ 受注ヘッダ(顧客、日付)
|
|
840
|
+
+ 受注明細(商品、数量、単価)
|
|
841
|
+
--
|
|
842
|
+
1:N の関係
|
|
843
|
+
明細の動的追加・削除
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
class "計算処理" as calculation {
|
|
847
|
+
+ 明細金額 = 数量 × 単価
|
|
848
|
+
+ 合計金額 = Σ 明細金額
|
|
849
|
+
+ 税額計算
|
|
850
|
+
--
|
|
851
|
+
リアルタイム再計算
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
class "ステータス管理" as status {
|
|
855
|
+
+ 受注 → 出荷 → 売上
|
|
856
|
+
+ ステータスによる編集制限
|
|
857
|
+
--
|
|
858
|
+
業務フロー制御
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
class "帳票出力" as report {
|
|
862
|
+
+ 納品書
|
|
863
|
+
+ 請求書
|
|
864
|
+
--
|
|
865
|
+
JasperReports 連携
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
header_detail --> calculation : "明細変更時"
|
|
870
|
+
status --> header_detail : "編集可否"
|
|
871
|
+
header_detail --> report : "印刷"
|
|
872
|
+
|
|
873
|
+
@enduml
|
|
874
|
+
```
|
|
875
|
+
|
|
876
|
+
### 16.2 受注入力画面の設計
|
|
877
|
+
|
|
878
|
+
```plantuml
|
|
879
|
+
@startuml order_entry_screen
|
|
880
|
+
|
|
881
|
+
package "受注入力画面" {
|
|
882
|
+
|
|
883
|
+
class "OrderEntryController" as controller {
|
|
884
|
+
+ customerComboBox
|
|
885
|
+
+ orderDatePicker
|
|
886
|
+
+ detailTableView
|
|
887
|
+
+ totalAmountLabel
|
|
888
|
+
--
|
|
889
|
+
onCustomerChange()
|
|
890
|
+
onAddDetail()
|
|
891
|
+
onRemoveDetail()
|
|
892
|
+
onSave()
|
|
893
|
+
onPrint()
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
class "OrderViewModel" as vm {
|
|
897
|
+
+ selectedCustomer
|
|
898
|
+
+ orderDate
|
|
899
|
+
+ orderDetails (ObservableList)
|
|
900
|
+
+ totalAmount (calculated)
|
|
901
|
+
--
|
|
902
|
+
addDetail()
|
|
903
|
+
removeDetail()
|
|
904
|
+
calculateTotal()
|
|
905
|
+
save()
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
class "OrderDetailRow" as detail {
|
|
909
|
+
+ productCode
|
|
910
|
+
+ productName
|
|
911
|
+
+ quantity
|
|
912
|
+
+ unitPrice
|
|
913
|
+
+ amount (calculated)
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
controller --> vm : "バインド"
|
|
918
|
+
vm "1" --> "*" detail : "contains"
|
|
919
|
+
|
|
920
|
+
note right of detail
|
|
921
|
+
明細行の金額は
|
|
922
|
+
数量 × 単価 で
|
|
923
|
+
自動計算
|
|
924
|
+
end note
|
|
925
|
+
|
|
926
|
+
@enduml
|
|
927
|
+
```
|
|
928
|
+
|
|
929
|
+
### 16.3 受注入力画面の TDD 実装
|
|
930
|
+
|
|
931
|
+
#### Red: 失敗するテストを書く
|
|
932
|
+
|
|
933
|
+
<details>
|
|
934
|
+
<summary>コード例: OrderEntryControllerTest.java</summary>
|
|
935
|
+
|
|
936
|
+
```java
|
|
937
|
+
@ExtendWith({ApplicationExtension.class, MockitoExtension.class})
|
|
938
|
+
@DisplayName("受注入力画面")
|
|
939
|
+
class OrderEntryControllerTest {
|
|
940
|
+
|
|
941
|
+
@Mock
|
|
942
|
+
private OrderUseCase orderUseCase;
|
|
943
|
+
@Mock
|
|
944
|
+
private ProductUseCase productUseCase;
|
|
945
|
+
|
|
946
|
+
private OrderViewModel viewModel;
|
|
947
|
+
|
|
948
|
+
@Nested
|
|
949
|
+
@DisplayName("明細追加")
|
|
950
|
+
class AddDetail {
|
|
951
|
+
|
|
952
|
+
@Test
|
|
953
|
+
@DisplayName("商品を選択して明細を追加できる")
|
|
954
|
+
void shouldAddDetailWhenProductSelected(FxRobot robot) {
|
|
955
|
+
when(productUseCase.findByCode("P001")).thenReturn(
|
|
956
|
+
createProduct("P001", "テスト商品", new BigDecimal("500"))
|
|
957
|
+
);
|
|
958
|
+
|
|
959
|
+
robot.clickOn("#productCodeField").write("P001");
|
|
960
|
+
robot.clickOn("#quantityField").write("10");
|
|
961
|
+
robot.clickOn("#addDetailButton");
|
|
962
|
+
|
|
963
|
+
TableView<OrderDetailRow> table = robot.lookup("#detailTable").queryTableView();
|
|
964
|
+
assertThat(table.getItems()).hasSize(1);
|
|
965
|
+
assertThat(table.getItems().get(0).getAmount())
|
|
966
|
+
.isEqualByComparingTo(new BigDecimal("5000"));
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
@Nested
|
|
971
|
+
@DisplayName("合計計算")
|
|
972
|
+
class TotalCalculation {
|
|
973
|
+
|
|
974
|
+
@Test
|
|
975
|
+
@DisplayName("明細追加時に合計金額が再計算される")
|
|
976
|
+
void shouldRecalculateTotalWhenDetailAdded(FxRobot robot) {
|
|
977
|
+
viewModel.addDetail(createDetailRow("P001", 10, new BigDecimal("500")));
|
|
978
|
+
viewModel.addDetail(createDetailRow("P002", 5, new BigDecimal("800")));
|
|
979
|
+
|
|
980
|
+
Label totalLabel = robot.lookup("#totalAmountLabel").queryAs(Label.class);
|
|
981
|
+
// 10 × 500 + 5 × 800 = 9000
|
|
982
|
+
assertThat(totalLabel.getText()).contains("9,000");
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
```
|
|
987
|
+
|
|
988
|
+
</details>
|
|
989
|
+
|
|
990
|
+
#### order-entry.fxml
|
|
991
|
+
|
|
992
|
+
<details>
|
|
993
|
+
<summary>コード例: order-entry.fxml</summary>
|
|
994
|
+
|
|
995
|
+
```xml
|
|
996
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
997
|
+
<?import javafx.scene.control.*?>
|
|
998
|
+
<?import javafx.scene.layout.*?>
|
|
999
|
+
<?import org.kordamp.ikonli.javafx.FontIcon?>
|
|
1000
|
+
|
|
1001
|
+
<VBox xmlns="http://javafx.com/javafx/21"
|
|
1002
|
+
xmlns:fx="http://javafx.com/fxml/1"
|
|
1003
|
+
fx:controller="com.example.sms.infrastructure.in.javafx.controller.OrderEntryController"
|
|
1004
|
+
spacing="10">
|
|
1005
|
+
|
|
1006
|
+
<!-- ヘッダ情報 -->
|
|
1007
|
+
<GridPane hgap="10" vgap="10" styleClass="header-form">
|
|
1008
|
+
<Label text="受注番号:" GridPane.columnIndex="0" GridPane.rowIndex="0"/>
|
|
1009
|
+
<TextField fx:id="orderNumberField" editable="false" GridPane.columnIndex="1"/>
|
|
1010
|
+
|
|
1011
|
+
<Label text="受注日:" GridPane.columnIndex="2" GridPane.rowIndex="0"/>
|
|
1012
|
+
<DatePicker fx:id="orderDatePicker" GridPane.columnIndex="3"/>
|
|
1013
|
+
|
|
1014
|
+
<Label text="顧客:" GridPane.columnIndex="0" GridPane.rowIndex="1"/>
|
|
1015
|
+
<HBox spacing="5" GridPane.columnIndex="1" GridPane.columnSpan="3">
|
|
1016
|
+
<TextField fx:id="customerCodeField" prefWidth="100"/>
|
|
1017
|
+
<Button onAction="#onSearchCustomer">
|
|
1018
|
+
<graphic><FontIcon iconLiteral="fas-search"/></graphic>
|
|
1019
|
+
</Button>
|
|
1020
|
+
<TextField fx:id="customerNameField" editable="false" HBox.hgrow="ALWAYS"/>
|
|
1021
|
+
</HBox>
|
|
1022
|
+
</GridPane>
|
|
1023
|
+
|
|
1024
|
+
<Separator/>
|
|
1025
|
+
|
|
1026
|
+
<!-- 明細入力エリア -->
|
|
1027
|
+
<HBox spacing="10" alignment="CENTER_LEFT">
|
|
1028
|
+
<Label text="商品:"/>
|
|
1029
|
+
<TextField fx:id="productCodeField" prefWidth="100"/>
|
|
1030
|
+
<TextField fx:id="productNameField" prefWidth="200" editable="false"/>
|
|
1031
|
+
<Label text="数量:"/>
|
|
1032
|
+
<TextField fx:id="quantityField" prefWidth="80" alignment="CENTER_RIGHT"/>
|
|
1033
|
+
<Label text="単価:"/>
|
|
1034
|
+
<TextField fx:id="unitPriceField" prefWidth="100" alignment="CENTER_RIGHT"/>
|
|
1035
|
+
<Button fx:id="addDetailButton" text="追加" styleClass="primary" onAction="#onAddDetail">
|
|
1036
|
+
<graphic><FontIcon iconLiteral="fas-plus"/></graphic>
|
|
1037
|
+
</Button>
|
|
1038
|
+
</HBox>
|
|
1039
|
+
|
|
1040
|
+
<!-- 明細テーブル -->
|
|
1041
|
+
<TableView fx:id="detailTable" VBox.vgrow="ALWAYS" editable="true">
|
|
1042
|
+
<columns>
|
|
1043
|
+
<TableColumn text="No." prefWidth="50"/>
|
|
1044
|
+
<TableColumn text="商品コード" prefWidth="100"/>
|
|
1045
|
+
<TableColumn text="商品名" prefWidth="200"/>
|
|
1046
|
+
<TableColumn text="数量" prefWidth="80" styleClass="quantity-column"/>
|
|
1047
|
+
<TableColumn text="単価" prefWidth="100" styleClass="amount-column"/>
|
|
1048
|
+
<TableColumn text="金額" prefWidth="120" styleClass="amount-column"/>
|
|
1049
|
+
<TableColumn text="削除" prefWidth="60"/>
|
|
1050
|
+
</columns>
|
|
1051
|
+
</TableView>
|
|
1052
|
+
|
|
1053
|
+
<!-- 合計エリア -->
|
|
1054
|
+
<HBox spacing="20" alignment="CENTER_RIGHT" styleClass="total-area">
|
|
1055
|
+
<VBox alignment="CENTER_RIGHT" spacing="5">
|
|
1056
|
+
<HBox spacing="10" alignment="CENTER_RIGHT">
|
|
1057
|
+
<Label text="小計:"/>
|
|
1058
|
+
<Label fx:id="subtotalLabel" text="¥0"/>
|
|
1059
|
+
</HBox>
|
|
1060
|
+
<HBox spacing="10" alignment="CENTER_RIGHT">
|
|
1061
|
+
<Label text="消費税:"/>
|
|
1062
|
+
<Label fx:id="taxAmountLabel" text="¥0"/>
|
|
1063
|
+
</HBox>
|
|
1064
|
+
<Separator/>
|
|
1065
|
+
<HBox spacing="10" alignment="CENTER_RIGHT">
|
|
1066
|
+
<Label text="合計金額:" styleClass="total-label-large"/>
|
|
1067
|
+
<Label fx:id="totalAmountLabel" text="¥0" styleClass="total-value-large"/>
|
|
1068
|
+
</HBox>
|
|
1069
|
+
</VBox>
|
|
1070
|
+
</HBox>
|
|
1071
|
+
|
|
1072
|
+
<!-- ボタンエリア -->
|
|
1073
|
+
<HBox spacing="10" alignment="CENTER_RIGHT">
|
|
1074
|
+
<Button text="クリア" onAction="#onClear"/>
|
|
1075
|
+
<Button text="印刷" onAction="#onPrint">
|
|
1076
|
+
<graphic><FontIcon iconLiteral="fas-print"/></graphic>
|
|
1077
|
+
</Button>
|
|
1078
|
+
<Button fx:id="saveButton" text="登録" styleClass="primary" onAction="#onSave">
|
|
1079
|
+
<graphic><FontIcon iconLiteral="fas-save"/></graphic>
|
|
1080
|
+
</Button>
|
|
1081
|
+
</HBox>
|
|
1082
|
+
</VBox>
|
|
1083
|
+
```
|
|
1084
|
+
|
|
1085
|
+
</details>
|
|
1086
|
+
|
|
1087
|
+
---
|
|
1088
|
+
|
|
1089
|
+
## 第17章:エラーハンドリングとドキュメント
|
|
1090
|
+
|
|
1091
|
+
### 17.1 AlertHelper の実装
|
|
1092
|
+
|
|
1093
|
+
アラートダイアログを統一的に扱うためのヘルパークラスを作成します。
|
|
1094
|
+
|
|
1095
|
+
<details>
|
|
1096
|
+
<summary>コード例: AlertHelper.java</summary>
|
|
1097
|
+
|
|
1098
|
+
```java
|
|
1099
|
+
package com.example.sms.infrastructure.in.javafx.util;
|
|
1100
|
+
|
|
1101
|
+
import javafx.scene.control.Alert;
|
|
1102
|
+
import javafx.scene.control.ButtonType;
|
|
1103
|
+
import javafx.scene.control.TextArea;
|
|
1104
|
+
import javafx.scene.layout.GridPane;
|
|
1105
|
+
import javafx.scene.layout.Priority;
|
|
1106
|
+
|
|
1107
|
+
import java.io.PrintWriter;
|
|
1108
|
+
import java.io.StringWriter;
|
|
1109
|
+
import java.util.Optional;
|
|
1110
|
+
|
|
1111
|
+
public class AlertHelper {
|
|
1112
|
+
|
|
1113
|
+
private AlertHelper() {}
|
|
1114
|
+
|
|
1115
|
+
public static void showInfo(String message) {
|
|
1116
|
+
Alert alert = new Alert(Alert.AlertType.INFORMATION);
|
|
1117
|
+
alert.setTitle("情報");
|
|
1118
|
+
alert.setHeaderText(null);
|
|
1119
|
+
alert.setContentText(message);
|
|
1120
|
+
alert.showAndWait();
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
public static void showWarning(String message) {
|
|
1124
|
+
Alert alert = new Alert(Alert.AlertType.WARNING);
|
|
1125
|
+
alert.setTitle("警告");
|
|
1126
|
+
alert.setHeaderText(null);
|
|
1127
|
+
alert.setContentText(message);
|
|
1128
|
+
alert.showAndWait();
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
public static void showError(String message) {
|
|
1132
|
+
Alert alert = new Alert(Alert.AlertType.ERROR);
|
|
1133
|
+
alert.setTitle("エラー");
|
|
1134
|
+
alert.setHeaderText(null);
|
|
1135
|
+
alert.setContentText(message);
|
|
1136
|
+
alert.showAndWait();
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
public static void showException(String title, String header, Throwable exception) {
|
|
1140
|
+
Alert alert = new Alert(Alert.AlertType.ERROR);
|
|
1141
|
+
alert.setTitle(title);
|
|
1142
|
+
alert.setHeaderText(header);
|
|
1143
|
+
alert.setContentText(exception.getMessage());
|
|
1144
|
+
|
|
1145
|
+
StringWriter sw = new StringWriter();
|
|
1146
|
+
PrintWriter pw = new PrintWriter(sw);
|
|
1147
|
+
exception.printStackTrace(pw);
|
|
1148
|
+
|
|
1149
|
+
TextArea textArea = new TextArea(sw.toString());
|
|
1150
|
+
textArea.setEditable(false);
|
|
1151
|
+
textArea.setWrapText(true);
|
|
1152
|
+
textArea.setMaxWidth(Double.MAX_VALUE);
|
|
1153
|
+
textArea.setMaxHeight(Double.MAX_VALUE);
|
|
1154
|
+
GridPane.setVgrow(textArea, Priority.ALWAYS);
|
|
1155
|
+
GridPane.setHgrow(textArea, Priority.ALWAYS);
|
|
1156
|
+
|
|
1157
|
+
GridPane expContent = new GridPane();
|
|
1158
|
+
expContent.setMaxWidth(Double.MAX_VALUE);
|
|
1159
|
+
expContent.add(textArea, 0, 0);
|
|
1160
|
+
|
|
1161
|
+
alert.getDialogPane().setExpandableContent(expContent);
|
|
1162
|
+
alert.showAndWait();
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
public static boolean showConfirmation(String message) {
|
|
1166
|
+
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
|
|
1167
|
+
alert.setTitle("確認");
|
|
1168
|
+
alert.setHeaderText(null);
|
|
1169
|
+
alert.setContentText(message);
|
|
1170
|
+
|
|
1171
|
+
Optional<ButtonType> result = alert.showAndWait();
|
|
1172
|
+
return result.isPresent() && result.get() == ButtonType.OK;
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
```
|
|
1176
|
+
|
|
1177
|
+
</details>
|
|
1178
|
+
|
|
1179
|
+
### 17.2 グローバル例外ハンドラ
|
|
1180
|
+
|
|
1181
|
+
<details>
|
|
1182
|
+
<summary>コード例: グローバル例外ハンドラ</summary>
|
|
1183
|
+
|
|
1184
|
+
```java
|
|
1185
|
+
public class JavaFxApplication extends Application {
|
|
1186
|
+
|
|
1187
|
+
private static final Logger logger = LoggerFactory.getLogger(JavaFxApplication.class);
|
|
1188
|
+
|
|
1189
|
+
@Override
|
|
1190
|
+
public void init() {
|
|
1191
|
+
Thread.setDefaultUncaughtExceptionHandler(this::handleUncaughtException);
|
|
1192
|
+
// ...
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
private void handleUncaughtException(Thread thread, Throwable throwable) {
|
|
1196
|
+
logger.error("未処理例外が発生しました [Thread: {}]", thread.getName(), throwable);
|
|
1197
|
+
|
|
1198
|
+
if (Platform.isFxApplicationThread()) {
|
|
1199
|
+
showErrorDialog(throwable);
|
|
1200
|
+
} else {
|
|
1201
|
+
Platform.runLater(() -> showErrorDialog(throwable));
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
private void showErrorDialog(Throwable throwable) {
|
|
1206
|
+
AlertHelper.showException(
|
|
1207
|
+
"予期しないエラー",
|
|
1208
|
+
"アプリケーションで予期しないエラーが発生しました",
|
|
1209
|
+
throwable
|
|
1210
|
+
);
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
```
|
|
1214
|
+
|
|
1215
|
+
</details>
|
|
1216
|
+
|
|
1217
|
+
### 17.3 ビジネス例外クラス
|
|
1218
|
+
|
|
1219
|
+
<details>
|
|
1220
|
+
<summary>コード例: ビジネス例外クラス</summary>
|
|
1221
|
+
|
|
1222
|
+
```java
|
|
1223
|
+
public class BusinessException extends RuntimeException {
|
|
1224
|
+
private final String errorCode;
|
|
1225
|
+
|
|
1226
|
+
public BusinessException(String errorCode, String message) {
|
|
1227
|
+
super(message);
|
|
1228
|
+
this.errorCode = errorCode;
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
public String getErrorCode() { return errorCode; }
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
public class EntityNotFoundException extends BusinessException {
|
|
1235
|
+
public EntityNotFoundException(String entityName, Object id) {
|
|
1236
|
+
super("NOT_FOUND", String.format("%s が見つかりません: %s", entityName, id));
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
public class ValidationException extends BusinessException {
|
|
1241
|
+
private final List<String> errors;
|
|
1242
|
+
|
|
1243
|
+
public ValidationException(List<String> errors) {
|
|
1244
|
+
super("VALIDATION_ERROR", String.join(", ", errors));
|
|
1245
|
+
this.errors = errors;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
public List<String> getErrors() { return errors; }
|
|
1249
|
+
}
|
|
1250
|
+
```
|
|
1251
|
+
|
|
1252
|
+
</details>
|
|
1253
|
+
|
|
1254
|
+
---
|
|
1255
|
+
|
|
1256
|
+
## Part 10-C のまとめ
|
|
1257
|
+
|
|
1258
|
+
### 実装した機能一覧
|
|
1259
|
+
|
|
1260
|
+
| 章 | 内容 |
|
|
1261
|
+
|----|------|
|
|
1262
|
+
| **第14章: 基礎** | JavaFX + Spring Boot 統合、MVVM パターン、Property と DataBinding |
|
|
1263
|
+
| **第15章: マスタ画面** | 商品・取引先マスタ CRUD、TestFX による TDD、Excel エクスポート |
|
|
1264
|
+
| **第16章: トランザクション画面** | 受注・出荷・請求処理、ヘッダ・明細構造、JasperReports 帳票出力 |
|
|
1265
|
+
| **第17章: エラーハンドリング** | 統一的なアラート表示、バリデーション、ロギング |
|
|
1266
|
+
|
|
1267
|
+
### アーキテクチャの特徴
|
|
1268
|
+
|
|
1269
|
+
```plantuml
|
|
1270
|
+
@startuml javafx_architecture
|
|
1271
|
+
|
|
1272
|
+
skinparam backgroundColor #FEFEFE
|
|
1273
|
+
skinparam packageStyle rectangle
|
|
1274
|
+
|
|
1275
|
+
package "UI Layer" {
|
|
1276
|
+
package "JavaFX UI" as javafx {
|
|
1277
|
+
[FXML/CSS] as fxml
|
|
1278
|
+
}
|
|
1279
|
+
package "REST API UI" as rest {
|
|
1280
|
+
[Controller] as controller
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
[ViewModel] as viewmodel
|
|
1284
|
+
[DTO/Response] as dto
|
|
1285
|
+
|
|
1286
|
+
fxml --> viewmodel
|
|
1287
|
+
controller --> dto
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
package "Application Layer" {
|
|
1291
|
+
[UseCase\n(ProductUseCase, OrderUseCase, etc.)] as usecase
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
package "Domain Layer" {
|
|
1295
|
+
[Entity\n(Product)] as entity
|
|
1296
|
+
[Value Object] as vo
|
|
1297
|
+
[Repository\n(Port)] as repo_port
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
package "Infrastructure Layer" {
|
|
1301
|
+
[MyBatis Mapper\n(Repository Implementation)] as mapper
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
viewmodel --> usecase
|
|
1305
|
+
dto --> usecase
|
|
1306
|
+
usecase --> entity
|
|
1307
|
+
usecase --> vo
|
|
1308
|
+
usecase --> repo_port
|
|
1309
|
+
repo_port <|.. mapper : implements
|
|
1310
|
+
|
|
1311
|
+
@enduml
|
|
1312
|
+
```
|
|
1313
|
+
|
|
1314
|
+
### 技術スタック
|
|
1315
|
+
|
|
1316
|
+
| カテゴリ | 技術 |
|
|
1317
|
+
|---------|------|
|
|
1318
|
+
| **言語** | Java 21 |
|
|
1319
|
+
| **フレームワーク** | Spring Boot 3.4, JavaFX 21 |
|
|
1320
|
+
| **ORM** | MyBatis 3.0 |
|
|
1321
|
+
| **データベース** | PostgreSQL 16 |
|
|
1322
|
+
| **帳票** | JasperReports |
|
|
1323
|
+
| **Excel** | Apache POI |
|
|
1324
|
+
| **テスト** | JUnit 5, TestFX |
|
|
1325
|
+
|
|
1326
|
+
### API サーバー版との比較
|
|
1327
|
+
|
|
1328
|
+
| 観点 | API サーバー版 | デスクトップ版 |
|
|
1329
|
+
|------|--------------|---------------|
|
|
1330
|
+
| **UI** | React/Vue | JavaFX + FXML |
|
|
1331
|
+
| **通信** | HTTP/REST | 直接呼び出し |
|
|
1332
|
+
| **状態管理** | Redux/Vuex | JavaFX Property |
|
|
1333
|
+
| **配置** | サーバー + クライアント | スタンドアロン |
|
|
1334
|
+
| **オフライン** | 不可 | 可能 |
|
|
1335
|
+
| **帳票** | サーバー生成 PDF | クライアント直接印刷 |
|
|
1336
|
+
|
|
1337
|
+
### デスクトップアプリケーションの選択基準
|
|
1338
|
+
|
|
1339
|
+
| 選択基準 | デスクトップ向き | Web 向き |
|
|
1340
|
+
|---------|----------------|---------|
|
|
1341
|
+
| オフライン要件 | 必須 | 不要 |
|
|
1342
|
+
| 帳票・印刷 | 高度な制御 | 基本的な印刷 |
|
|
1343
|
+
| レスポンス | 即座の応答 | ネットワーク許容 |
|
|
1344
|
+
| 配布・更新 | 限定的 | 全ユーザー同時 |
|
|
1345
|
+
| UI 自由度 | 高い | HTML/CSS 制約 |
|
|
1346
|
+
|
|
1347
|
+
JavaFX デスクトップアプリケーションは、オフライン環境での利用や高度な帳票出力が必要な業務システムに適しています。既存のヘキサゴナルアーキテクチャを活かしながら、Input Adapter として追加することで、REST API と並行運用も可能です。
|