@mytechtoday/augment-extensions 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (232) hide show
  1. package/LICENSE +22 -22
  2. package/README.md +105 -6
  3. package/augment-extensions/domain-rules/software-architecture/README.md +143 -143
  4. package/augment-extensions/domain-rules/software-architecture/examples/banking-layered.md +961 -961
  5. package/augment-extensions/domain-rules/software-architecture/examples/ecommerce-microservices.md +990 -990
  6. package/augment-extensions/domain-rules/software-architecture/examples/iot-eventdriven.md +882 -882
  7. package/augment-extensions/domain-rules/software-architecture/examples/monolith-to-microservices-migration.md +703 -703
  8. package/augment-extensions/domain-rules/software-architecture/examples/serverless-imageprocessing.md +957 -957
  9. package/augment-extensions/domain-rules/software-architecture/examples/trading-eventdriven.md +747 -747
  10. package/augment-extensions/domain-rules/software-architecture/module.json +119 -119
  11. package/augment-extensions/domain-rules/software-architecture/rules/challenges-solutions.md +763 -763
  12. package/augment-extensions/domain-rules/software-architecture/rules/definitions-terminology.md +409 -409
  13. package/augment-extensions/domain-rules/software-architecture/rules/design-principles.md +684 -684
  14. package/augment-extensions/domain-rules/software-architecture/rules/evaluation-testing.md +1381 -1381
  15. package/augment-extensions/domain-rules/software-architecture/rules/event-driven-architecture.md +616 -616
  16. package/augment-extensions/domain-rules/software-architecture/rules/fundamentals.md +306 -306
  17. package/augment-extensions/domain-rules/software-architecture/rules/industry-architectures.md +554 -554
  18. package/augment-extensions/domain-rules/software-architecture/rules/layered-architecture.md +776 -776
  19. package/augment-extensions/domain-rules/software-architecture/rules/microservices-architecture.md +503 -503
  20. package/augment-extensions/domain-rules/software-architecture/rules/modeling-documentation.md +1199 -1199
  21. package/augment-extensions/domain-rules/software-architecture/rules/monolithic-architecture.md +351 -351
  22. package/augment-extensions/domain-rules/software-architecture/rules/principles.md +556 -556
  23. package/augment-extensions/domain-rules/software-architecture/rules/quality-attributes.md +797 -797
  24. package/augment-extensions/domain-rules/software-architecture/rules/scalability-performance.md +1345 -1345
  25. package/augment-extensions/domain-rules/software-architecture/rules/security-architecture.md +1039 -1039
  26. package/augment-extensions/domain-rules/software-architecture/rules/serverless-architecture.md +711 -711
  27. package/augment-extensions/domain-rules/software-architecture/rules/skills-development.md +568 -568
  28. package/augment-extensions/domain-rules/software-architecture/rules/tools-methodologies.md +961 -961
  29. package/augment-extensions/visual-design/CHANGELOG.md +132 -132
  30. package/augment-extensions/visual-design/README.md +255 -255
  31. package/augment-extensions/visual-design/__tests__/README.md +119 -119
  32. package/augment-extensions/visual-design/__tests__/style-selector.test.ts +172 -172
  33. package/augment-extensions/visual-design/__tests__/vendor-styles.test.ts +214 -214
  34. package/augment-extensions/visual-design/domains/other/ai-prompt-helper.ts +157 -157
  35. package/augment-extensions/visual-design/domains/other/dotnet-application.ts +156 -156
  36. package/augment-extensions/visual-design/domains/other/linux-platform.ts +156 -156
  37. package/augment-extensions/visual-design/domains/other/mobile-application.ts +157 -157
  38. package/augment-extensions/visual-design/domains/other/motion-picture.ts +156 -156
  39. package/augment-extensions/visual-design/domains/other/os-application.ts +156 -156
  40. package/augment-extensions/visual-design/domains/other/print-campaigns.ts +158 -158
  41. package/augment-extensions/visual-design/domains/other/web-app.ts +157 -157
  42. package/augment-extensions/visual-design/domains/other/website.ts +161 -161
  43. package/augment-extensions/visual-design/domains/other/windows-platform.ts +156 -156
  44. package/augment-extensions/visual-design/domains/web-page-styles/amazon-cloudscape.ts +506 -506
  45. package/augment-extensions/visual-design/domains/web-page-styles/google-modern.ts +615 -615
  46. package/augment-extensions/visual-design/domains/web-page-styles/microsoft-fluent.ts +531 -531
  47. package/augment-extensions/visual-design/examples/README.md +97 -97
  48. package/augment-extensions/visual-design/examples/ai-prompt-generation.md +233 -233
  49. package/augment-extensions/visual-design/examples/basic-usage.md +216 -216
  50. package/augment-extensions/visual-design/examples/domain-workflows.md +257 -257
  51. package/augment-extensions/visual-design/examples/vendor-comparison.md +247 -247
  52. package/augment-extensions/visual-design/module.json +78 -78
  53. package/augment-extensions/visual-design/style-selector.ts +177 -177
  54. package/augment-extensions/visual-design/types.ts +302 -302
  55. package/augment-extensions/visual-design/visual-design-core.ts +469 -469
  56. package/augment-extensions/workflows/adr-support/README.md +227 -227
  57. package/augment-extensions/workflows/adr-support/__tests__/adr-validator.test.ts +203 -203
  58. package/augment-extensions/workflows/adr-support/adr-validator.ts +162 -162
  59. package/augment-extensions/workflows/adr-support/examples/complete-lifecycle-example.md +449 -449
  60. package/augment-extensions/workflows/adr-support/examples/integration-example.md +580 -580
  61. package/augment-extensions/workflows/adr-support/examples/superseding-example.md +436 -436
  62. package/augment-extensions/workflows/adr-support/module.json +112 -112
  63. package/augment-extensions/workflows/adr-support/rules/adr-creation.md +372 -372
  64. package/augment-extensions/workflows/adr-support/rules/beads-integration.md +443 -443
  65. package/augment-extensions/workflows/adr-support/rules/conflict-detection.md +486 -486
  66. package/augment-extensions/workflows/adr-support/rules/decision-detection.md +362 -362
  67. package/augment-extensions/workflows/adr-support/rules/lifecycle-management.md +427 -427
  68. package/augment-extensions/workflows/adr-support/rules/openspec-integration.md +465 -465
  69. package/augment-extensions/workflows/adr-support/rules/template-selection.md +405 -405
  70. package/augment-extensions/workflows/adr-support/rules/validation-rules.md +543 -543
  71. package/augment-extensions/workflows/adr-support/schemas/adr-config.json +191 -191
  72. package/augment-extensions/workflows/adr-support/schemas/adr-metadata.json +172 -172
  73. package/augment-extensions/workflows/adr-support/templates/business-case.md +235 -235
  74. package/augment-extensions/workflows/adr-support/templates/madr-elaborate.md +197 -197
  75. package/augment-extensions/workflows/adr-support/templates/madr-simple.md +68 -68
  76. package/augment-extensions/workflows/adr-support/templates/nygard.md +84 -84
  77. package/augment-extensions/writing-standards/screenplay/rules/file-organization.md +213 -213
  78. package/augment-extensions/writing-standards/screenplay/utils/__tests__/file-organization.test.ts +169 -169
  79. package/augment-extensions/writing-standards/screenplay/utils/file-organization.ts +165 -165
  80. package/cli/dist/commands/agent.d.ts +37 -0
  81. package/cli/dist/commands/agent.d.ts.map +1 -0
  82. package/cli/dist/commands/agent.js +222 -0
  83. package/cli/dist/commands/agent.js.map +1 -0
  84. package/cli/dist/commands/beads.d.ts +64 -0
  85. package/cli/dist/commands/beads.d.ts.map +1 -0
  86. package/cli/dist/commands/beads.js +377 -0
  87. package/cli/dist/commands/beads.js.map +1 -0
  88. package/cli/dist/commands/change.d.ts +54 -0
  89. package/cli/dist/commands/change.d.ts.map +1 -0
  90. package/cli/dist/commands/change.js +243 -0
  91. package/cli/dist/commands/change.js.map +1 -0
  92. package/cli/dist/commands/clean.d.ts +15 -0
  93. package/cli/dist/commands/clean.d.ts.map +1 -0
  94. package/cli/dist/commands/clean.js +63 -0
  95. package/cli/dist/commands/clean.js.map +1 -0
  96. package/cli/dist/commands/clone.d.ts +15 -0
  97. package/cli/dist/commands/clone.d.ts.map +1 -0
  98. package/cli/dist/commands/clone.js +49 -0
  99. package/cli/dist/commands/clone.js.map +1 -0
  100. package/cli/dist/commands/config.d.ts +33 -0
  101. package/cli/dist/commands/config.d.ts.map +1 -0
  102. package/cli/dist/commands/config.js +166 -0
  103. package/cli/dist/commands/config.js.map +1 -0
  104. package/cli/dist/commands/context.d.ts +38 -0
  105. package/cli/dist/commands/context.d.ts.map +1 -0
  106. package/cli/dist/commands/context.js +205 -0
  107. package/cli/dist/commands/context.js.map +1 -0
  108. package/cli/dist/commands/create.d.ts +18 -0
  109. package/cli/dist/commands/create.d.ts.map +1 -0
  110. package/cli/dist/commands/create.js +178 -0
  111. package/cli/dist/commands/create.js.map +1 -0
  112. package/cli/dist/commands/diff.d.ts +19 -0
  113. package/cli/dist/commands/diff.d.ts.map +1 -0
  114. package/cli/dist/commands/diff.js +104 -0
  115. package/cli/dist/commands/diff.js.map +1 -0
  116. package/cli/dist/commands/doctor.d.ts +14 -0
  117. package/cli/dist/commands/doctor.d.ts.map +1 -0
  118. package/cli/dist/commands/doctor.js +62 -0
  119. package/cli/dist/commands/doctor.js.map +1 -0
  120. package/cli/dist/commands/export.d.ts +28 -0
  121. package/cli/dist/commands/export.d.ts.map +1 -0
  122. package/cli/dist/commands/export.js +135 -0
  123. package/cli/dist/commands/export.js.map +1 -0
  124. package/cli/dist/commands/import.d.ts +23 -0
  125. package/cli/dist/commands/import.d.ts.map +1 -0
  126. package/cli/dist/commands/import.js +118 -0
  127. package/cli/dist/commands/import.js.map +1 -0
  128. package/cli/dist/commands/prompt.d.ts +45 -0
  129. package/cli/dist/commands/prompt.d.ts.map +1 -0
  130. package/cli/dist/commands/prompt.js +223 -0
  131. package/cli/dist/commands/prompt.js.map +1 -0
  132. package/cli/dist/commands/spec.d.ts +57 -0
  133. package/cli/dist/commands/spec.d.ts.map +1 -0
  134. package/cli/dist/commands/spec.js +279 -0
  135. package/cli/dist/commands/spec.js.map +1 -0
  136. package/cli/dist/commands/stats.d.ts +18 -0
  137. package/cli/dist/commands/stats.d.ts.map +1 -0
  138. package/cli/dist/commands/stats.js +85 -0
  139. package/cli/dist/commands/stats.js.map +1 -0
  140. package/cli/dist/commands/task.d.ts +65 -0
  141. package/cli/dist/commands/task.d.ts.map +1 -0
  142. package/cli/dist/commands/task.js +282 -0
  143. package/cli/dist/commands/task.js.map +1 -0
  144. package/cli/dist/commands/template.d.ts +17 -0
  145. package/cli/dist/commands/template.d.ts.map +1 -0
  146. package/cli/dist/commands/template.js +55 -0
  147. package/cli/dist/commands/template.js.map +1 -0
  148. package/cli/dist/utils/agent-config.d.ts +129 -0
  149. package/cli/dist/utils/agent-config.d.ts.map +1 -0
  150. package/cli/dist/utils/agent-config.js +297 -0
  151. package/cli/dist/utils/agent-config.js.map +1 -0
  152. package/cli/dist/utils/auto-sync.js +19 -19
  153. package/cli/dist/utils/beads-graph.d.ts +17 -0
  154. package/cli/dist/utils/beads-graph.d.ts.map +1 -0
  155. package/cli/dist/utils/beads-graph.js +150 -0
  156. package/cli/dist/utils/beads-graph.js.map +1 -0
  157. package/cli/dist/utils/beads-integration.d.ts +112 -0
  158. package/cli/dist/utils/beads-integration.d.ts.map +1 -0
  159. package/cli/dist/utils/beads-integration.js +312 -0
  160. package/cli/dist/utils/beads-integration.js.map +1 -0
  161. package/cli/dist/utils/beads-reporter.d.ts +17 -0
  162. package/cli/dist/utils/beads-reporter.d.ts.map +1 -0
  163. package/cli/dist/utils/beads-reporter.js +160 -0
  164. package/cli/dist/utils/beads-reporter.js.map +1 -0
  165. package/cli/dist/utils/cache-manager.d.ts +55 -0
  166. package/cli/dist/utils/cache-manager.d.ts.map +1 -0
  167. package/cli/dist/utils/cache-manager.js +150 -0
  168. package/cli/dist/utils/cache-manager.js.map +1 -0
  169. package/cli/dist/utils/change-manager.d.ts +70 -0
  170. package/cli/dist/utils/change-manager.d.ts.map +1 -0
  171. package/cli/dist/utils/change-manager.js +412 -0
  172. package/cli/dist/utils/change-manager.js.map +1 -0
  173. package/cli/dist/utils/config-manager-enhanced.d.ts +66 -0
  174. package/cli/dist/utils/config-manager-enhanced.d.ts.map +1 -0
  175. package/cli/dist/utils/config-manager-enhanced.js +77 -0
  176. package/cli/dist/utils/config-manager-enhanced.js.map +1 -0
  177. package/cli/dist/utils/context-manager.d.ts +96 -0
  178. package/cli/dist/utils/context-manager.d.ts.map +1 -0
  179. package/cli/dist/utils/context-manager.js +258 -0
  180. package/cli/dist/utils/context-manager.js.map +1 -0
  181. package/cli/dist/utils/diff-engine.d.ts +78 -0
  182. package/cli/dist/utils/diff-engine.d.ts.map +1 -0
  183. package/cli/dist/utils/diff-engine.js +233 -0
  184. package/cli/dist/utils/diff-engine.js.map +1 -0
  185. package/cli/dist/utils/export-system.d.ts +101 -0
  186. package/cli/dist/utils/export-system.d.ts.map +1 -0
  187. package/cli/dist/utils/export-system.js +289 -0
  188. package/cli/dist/utils/export-system.js.map +1 -0
  189. package/cli/dist/utils/health-checker.d.ts +66 -0
  190. package/cli/dist/utils/health-checker.d.ts.map +1 -0
  191. package/cli/dist/utils/health-checker.js +285 -0
  192. package/cli/dist/utils/health-checker.js.map +1 -0
  193. package/cli/dist/utils/import-system.d.ts +74 -0
  194. package/cli/dist/utils/import-system.d.ts.map +1 -0
  195. package/cli/dist/utils/import-system.js +317 -0
  196. package/cli/dist/utils/import-system.js.map +1 -0
  197. package/cli/dist/utils/module-cloner.d.ts +40 -0
  198. package/cli/dist/utils/module-cloner.d.ts.map +1 -0
  199. package/cli/dist/utils/module-cloner.js +136 -0
  200. package/cli/dist/utils/module-cloner.js.map +1 -0
  201. package/cli/dist/utils/prompt-manager.d.ts +90 -0
  202. package/cli/dist/utils/prompt-manager.d.ts.map +1 -0
  203. package/cli/dist/utils/prompt-manager.js +302 -0
  204. package/cli/dist/utils/prompt-manager.js.map +1 -0
  205. package/cli/dist/utils/spec-manager.d.ts +65 -0
  206. package/cli/dist/utils/spec-manager.d.ts.map +1 -0
  207. package/cli/dist/utils/spec-manager.js +329 -0
  208. package/cli/dist/utils/spec-manager.js.map +1 -0
  209. package/cli/dist/utils/stats-collector.d.ts +74 -0
  210. package/cli/dist/utils/stats-collector.d.ts.map +1 -0
  211. package/cli/dist/utils/stats-collector.js +164 -0
  212. package/cli/dist/utils/stats-collector.js.map +1 -0
  213. package/cli/dist/utils/template-engine.d.ts +47 -0
  214. package/cli/dist/utils/template-engine.d.ts.map +1 -0
  215. package/cli/dist/utils/template-engine.js +204 -0
  216. package/cli/dist/utils/template-engine.js.map +1 -0
  217. package/package.json +12 -3
  218. package/augment-extensions/workflows/openspec/README.md +0 -96
  219. package/augment-extensions/workflows/openspec/examples/complete-change-example.md +0 -244
  220. package/augment-extensions/workflows/openspec/module.json +0 -54
  221. package/augment-extensions/workflows/openspec/rules/best-practices.md +0 -272
  222. package/augment-extensions/workflows/openspec/rules/manual-setup.md +0 -231
  223. package/augment-extensions/workflows/openspec/rules/spec-format.md +0 -236
  224. package/augment-extensions/workflows/openspec/rules/workflow.md +0 -214
  225. package/cli/dist/utils/__tests__/adr-validator.example.d.ts +0 -6
  226. package/cli/dist/utils/__tests__/adr-validator.example.d.ts.map +0 -1
  227. package/cli/dist/utils/__tests__/adr-validator.example.js +0 -148
  228. package/cli/dist/utils/__tests__/adr-validator.example.js.map +0 -1
  229. package/cli/dist/utils/adr-validator.d.ts +0 -65
  230. package/cli/dist/utils/adr-validator.d.ts.map +0 -1
  231. package/cli/dist/utils/adr-validator.js +0 -203
  232. package/cli/dist/utils/adr-validator.js.map +0 -1
@@ -1,961 +1,961 @@
1
- # Banking Application Architecture Example
2
-
3
- ## Overview
4
-
5
- This document provides a comprehensive example of a banking application built with layered architecture, focusing on security, compliance, and data integrity.
6
-
7
- ---
8
-
9
- ## System Context
10
-
11
- ### Business Requirements
12
-
13
- **Functional Requirements**
14
- - Account management (checking, savings, credit)
15
- - Fund transfers (internal and external)
16
- - Transaction history and statements
17
- - Bill payments and recurring payments
18
- - Loan applications and management
19
- - Customer authentication and authorization
20
- - Fraud detection and alerts
21
- - Regulatory reporting
22
-
23
- **Non-Functional Requirements**
24
- - **Security**: Multi-factor authentication, encryption at rest and in transit
25
- - **Compliance**: SOX, PCI DSS, GDPR, Basel III
26
- - **Availability**: 99.95% uptime (4.38 hours downtime/year)
27
- - **Data Integrity**: ACID transactions, zero data loss
28
- - **Auditability**: Complete audit trail for all transactions
29
- - **Performance**: Transaction processing < 500ms
30
-
31
- ### Scale Metrics
32
- - 500,000 active customers
33
- - 1 million accounts
34
- - 100,000 transactions per day
35
- - $10 billion in assets under management
36
- - 24/7 operation with global presence
37
-
38
- ---
39
-
40
- ## Architecture Overview
41
-
42
- ### Layered Architecture Pattern
43
-
44
- **Four-Tier Architecture**
45
-
46
- ```
47
- ┌─────────────────────────────────────────────────────────────┐
48
- │ Presentation Layer │
49
- │ Web UI, Mobile Apps, ATM Interface, APIs │
50
- └─────────────────────────────────────────────────────────────┘
51
-
52
- ┌─────────────────────────────────────────────────────────────┐
53
- │ Application Layer │
54
- │ Use Cases, Orchestration, Transaction Coordination │
55
- │ (Account Service, Transfer Service, Loan Service) │
56
- └─────────────────────────────────────────────────────────────┘
57
-
58
- ┌─────────────────────────────────────────────────────────────┐
59
- │ Domain Layer │
60
- │ Business Logic, Domain Models, Business Rules │
61
- │ (Account, Transaction, Customer, Loan) │
62
- └─────────────────────────────────────────────────────────────┘
63
-
64
- ┌─────────────────────────────────────────────────────────────┐
65
- │ Infrastructure Layer │
66
- │ Database, External Services, Messaging, Security │
67
- │ (PostgreSQL, Redis, Kafka, SWIFT, ACH) │
68
- └─────────────────────────────────────────────────────────────┘
69
- ```
70
-
71
- ### Layer Responsibilities
72
-
73
- **1. Presentation Layer**
74
- - Web application (React)
75
- - Mobile apps (iOS, Android)
76
- - ATM interface
77
- - Admin portal
78
- - RESTful APIs
79
- - Input validation
80
- - Session management
81
-
82
- **2. Application Layer**
83
- - Account management use cases
84
- - Transfer orchestration
85
- - Loan processing workflows
86
- - Transaction coordination
87
- - Event publishing
88
- - External service integration
89
-
90
- **3. Domain Layer**
91
- - Account entity and business rules
92
- - Transaction processing logic
93
- - Interest calculation
94
- - Overdraft protection
95
- - Fraud detection rules
96
- - Loan approval logic
97
-
98
- **4. Infrastructure Layer**
99
- - PostgreSQL database (ACID compliance)
100
- - Redis cache (session, rate limiting)
101
- - Kafka message broker (event streaming)
102
- - SWIFT integration (international transfers)
103
- - ACH integration (domestic transfers)
104
- - HSM (Hardware Security Module) for encryption
105
-
106
- ---
107
-
108
- ## Technology Stack
109
-
110
- ### Presentation Layer
111
- - **Web**: React, TypeScript, Material-UI
112
- - **Mobile**: React Native
113
- - **API**: Node.js, Express, GraphQL
114
-
115
- ### Application Layer
116
- - **Language**: Java 17
117
- - **Framework**: Spring Boot 3.x
118
- - **Security**: Spring Security, OAuth 2.0
119
-
120
- ### Domain Layer
121
- - **Language**: Java 17
122
- - **Patterns**: Domain-Driven Design (DDD)
123
- - **Validation**: Bean Validation (JSR 380)
124
-
125
- ### Infrastructure Layer
126
- - **Database**: PostgreSQL 15 (primary), PostgreSQL replicas (read)
127
- - **Cache**: Redis Cluster
128
- - **Message Broker**: Apache Kafka
129
- - **Search**: Elasticsearch (transaction search)
130
- - **File Storage**: AWS S3 (statements, documents)
131
-
132
- ### Security & Compliance
133
- - **Authentication**: OAuth 2.0, OpenID Connect
134
- - **MFA**: TOTP, SMS, Biometric
135
- - **Encryption**: AES-256 (at rest), TLS 1.3 (in transit)
136
- - **HSM**: Thales Luna HSM
137
- - **Audit**: Splunk, custom audit tables
138
-
139
- ### Infrastructure
140
- - **Hosting**: AWS (multi-region)
141
- - **Container**: Docker, Kubernetes (EKS)
142
- - **Load Balancer**: AWS ALB
143
- - **CDN**: CloudFront
144
- - **Monitoring**: Datadog, CloudWatch
145
-
146
- ---
147
-
148
- ## Implementation Details
149
-
150
- ### 1. Domain Layer - Account Entity
151
-
152
- **Rich Domain Model**
153
-
154
- ```java
155
- // src/main/java/com/bank/domain/account/Account.java
156
- package com.bank.domain.account;
157
-
158
- import com.bank.domain.common.Money;
159
- import com.bank.domain.transaction.Transaction;
160
- import javax.validation.constraints.*;
161
- import java.math.BigDecimal;
162
- import java.time.LocalDateTime;
163
- import java.util.ArrayList;
164
- import java.util.List;
165
-
166
- public class Account {
167
- @NotNull
168
- private final AccountId id;
169
-
170
- @NotNull
171
- private final CustomerId customerId;
172
-
173
- @NotNull
174
- private final AccountType type;
175
-
176
- @NotNull
177
- private Money balance;
178
-
179
- @NotNull
180
- private AccountStatus status;
181
-
182
- private Money overdraftLimit;
183
-
184
- @NotNull
185
- private final LocalDateTime createdAt;
186
-
187
- private LocalDateTime closedAt;
188
-
189
- private final List<Transaction> transactions = new ArrayList<>();
190
-
191
- // Business logic methods
192
- public void deposit(Money amount, String description) {
193
- validateAccountActive();
194
- validatePositiveAmount(amount);
195
-
196
- this.balance = this.balance.add(amount);
197
-
198
- Transaction transaction = Transaction.createDeposit(
199
- this.id,
200
- amount,
201
- description,
202
- LocalDateTime.now()
203
- );
204
-
205
- this.transactions.add(transaction);
206
- }
207
-
208
- public void withdraw(Money amount, String description) {
209
- validateAccountActive();
210
- validatePositiveAmount(amount);
211
- validateSufficientFunds(amount);
212
-
213
- this.balance = this.balance.subtract(amount);
214
-
215
- Transaction transaction = Transaction.createWithdrawal(
216
- this.id,
217
- amount,
218
- description,
219
- LocalDateTime.now()
220
- );
221
-
222
- this.transactions.add(transaction);
223
- }
224
-
225
- public void transfer(Account targetAccount, Money amount, String description) {
226
- validateAccountActive();
227
- targetAccount.validateAccountActive();
228
- validatePositiveAmount(amount);
229
- validateSufficientFunds(amount);
230
- validateDifferentAccounts(targetAccount);
231
-
232
- this.withdraw(amount, "Transfer to " + targetAccount.getId());
233
- targetAccount.deposit(amount, "Transfer from " + this.getId());
234
- }
235
-
236
- public Money calculateInterest(BigDecimal annualRate, int days) {
237
- if (this.type != AccountType.SAVINGS) {
238
- throw new IllegalStateException("Interest only applies to savings accounts");
239
- }
240
-
241
- BigDecimal dailyRate = annualRate.divide(BigDecimal.valueOf(365), 10, BigDecimal.ROUND_HALF_UP);
242
- BigDecimal interest = this.balance.getAmount()
243
- .multiply(dailyRate)
244
- .multiply(BigDecimal.valueOf(days));
245
-
246
- return new Money(interest, this.balance.getCurrency());
247
- }
248
-
249
- // Validation methods
250
- private void validateAccountActive() {
251
- if (this.status != AccountStatus.ACTIVE) {
252
- throw new AccountNotActiveException("Account " + this.id + " is not active");
253
- }
254
- }
255
-
256
- private void validatePositiveAmount(Money amount) {
257
- if (amount.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
258
- throw new IllegalArgumentException("Amount must be positive");
259
- }
260
- }
261
-
262
- private void validateSufficientFunds(Money amount) {
263
- Money availableBalance = this.balance.add(this.overdraftLimit != null ? this.overdraftLimit : Money.ZERO);
264
-
265
- if (availableBalance.compareTo(amount) < 0) {
266
- throw new InsufficientFundsException(
267
- "Insufficient funds. Available: " + availableBalance + ", Required: " + amount
268
- );
269
- }
270
- }
271
-
272
- private void validateDifferentAccounts(Account other) {
273
- if (this.id.equals(other.id)) {
274
- throw new IllegalArgumentException("Cannot transfer to the same account");
275
- }
276
- }
277
-
278
- // Getters
279
- public AccountId getId() { return id; }
280
- public Money getBalance() { return balance; }
281
- public AccountStatus getStatus() { return status; }
282
- }
283
-
284
- // Value Objects
285
- public record AccountId(String value) {
286
- public AccountId {
287
- if (value == null || value.isBlank()) {
288
- throw new IllegalArgumentException("Account ID cannot be null or blank");
289
- }
290
- }
291
- }
292
-
293
- public record Money(BigDecimal amount, Currency currency) implements Comparable<Money> {
294
- public static final Money ZERO = new Money(BigDecimal.ZERO, Currency.USD);
295
-
296
- public Money add(Money other) {
297
- validateSameCurrency(other);
298
- return new Money(this.amount.add(other.amount), this.currency);
299
- }
300
-
301
- public Money subtract(Money other) {
302
- validateSameCurrency(other);
303
- return new Money(this.amount.subtract(other.amount), this.currency);
304
- }
305
-
306
- @Override
307
- public int compareTo(Money other) {
308
- validateSameCurrency(other);
309
- return this.amount.compareTo(other.amount);
310
- }
311
-
312
- private void validateSameCurrency(Money other) {
313
- if (!this.currency.equals(other.currency)) {
314
- throw new IllegalArgumentException("Cannot operate on different currencies");
315
- }
316
- }
317
- }
318
-
319
- public enum AccountType {
320
- CHECKING, SAVINGS, CREDIT, LOAN
321
- }
322
-
323
- public enum AccountStatus {
324
- ACTIVE, SUSPENDED, CLOSED, FROZEN
325
- }
326
- ```
327
-
328
- ### 2. Application Layer - Transfer Service
329
-
330
- **Use Case Orchestration**
331
-
332
- ```java
333
- // src/main/java/com/bank/application/transfer/TransferService.java
334
- package com.bank.application.transfer;
335
-
336
- import com.bank.domain.account.*;
337
- import com.bank.domain.common.Money;
338
- import com.bank.infrastructure.events.EventPublisher;
339
- import com.bank.infrastructure.fraud.FraudDetectionService;
340
- import org.springframework.stereotype.Service;
341
- import org.springframework.transaction.annotation.Transactional;
342
- import java.time.LocalDateTime;
343
-
344
- @Service
345
- public class TransferService {
346
- private final AccountRepository accountRepository;
347
- private final TransferRepository transferRepository;
348
- private final FraudDetectionService fraudDetection;
349
- private final EventPublisher eventPublisher;
350
- private final AuditLogger auditLogger;
351
-
352
- public TransferService(
353
- AccountRepository accountRepository,
354
- TransferRepository transferRepository,
355
- FraudDetectionService fraudDetection,
356
- EventPublisher eventPublisher,
357
- AuditLogger auditLogger
358
- ) {
359
- this.accountRepository = accountRepository;
360
- this.transferRepository = transferRepository;
361
- this.fraudDetection = fraudDetection;
362
- this.eventPublisher = eventPublisher;
363
- this.auditLogger = auditLogger;
364
- }
365
-
366
- @Transactional
367
- public TransferResult executeTransfer(TransferRequest request) {
368
- // 1. Validate request
369
- validateTransferRequest(request);
370
-
371
- // 2. Load accounts
372
- Account sourceAccount = accountRepository.findById(request.sourceAccountId())
373
- .orElseThrow(() -> new AccountNotFoundException(request.sourceAccountId()));
374
-
375
- Account targetAccount = accountRepository.findById(request.targetAccountId())
376
- .orElseThrow(() -> new AccountNotFoundException(request.targetAccountId()));
377
-
378
- // 3. Fraud detection
379
- FraudCheckResult fraudCheck = fraudDetection.checkTransfer(
380
- sourceAccount,
381
- targetAccount,
382
- request.amount()
383
- );
384
-
385
- if (fraudCheck.isSuspicious()) {
386
- auditLogger.logSuspiciousActivity(request, fraudCheck);
387
- throw new SuspiciousActivityException("Transfer flagged for review");
388
- }
389
-
390
- // 4. Execute transfer (domain logic)
391
- sourceAccount.transfer(targetAccount, request.amount(), request.description());
392
-
393
- // 5. Save accounts
394
- accountRepository.save(sourceAccount);
395
- accountRepository.save(targetAccount);
396
-
397
- // 6. Create transfer record
398
- Transfer transfer = Transfer.create(
399
- sourceAccount.getId(),
400
- targetAccount.getId(),
401
- request.amount(),
402
- request.description(),
403
- TransferStatus.COMPLETED,
404
- LocalDateTime.now()
405
- );
406
- transferRepository.save(transfer);
407
-
408
- // 7. Publish event
409
- eventPublisher.publish(new TransferCompletedEvent(
410
- transfer.getId(),
411
- sourceAccount.getId(),
412
- targetAccount.getId(),
413
- request.amount(),
414
- LocalDateTime.now()
415
- ));
416
-
417
- // 8. Audit log
418
- auditLogger.logTransfer(request, transfer, "SUCCESS");
419
-
420
- return new TransferResult(transfer.getId(), TransferStatus.COMPLETED);
421
- }
422
-
423
- private void validateTransferRequest(TransferRequest request) {
424
- if (request.amount().getAmount().compareTo(BigDecimal.ZERO) <= 0) {
425
- throw new IllegalArgumentException("Transfer amount must be positive");
426
- }
427
-
428
- if (request.sourceAccountId().equals(request.targetAccountId())) {
429
- throw new IllegalArgumentException("Source and target accounts must be different");
430
- }
431
- }
432
- }
433
- ```
434
-
435
-
436
-
437
- ### 3. Infrastructure Layer - Repository Implementation
438
-
439
- **Data Access with JPA**
440
-
441
- ```java
442
- // src/main/java/com/bank/infrastructure/persistence/JpaAccountRepository.java
443
- package com.bank.infrastructure.persistence;
444
-
445
- import com.bank.domain.account.*;
446
- import org.springframework.data.jpa.repository.JpaRepository;
447
- import org.springframework.data.jpa.repository.Lock;
448
- import org.springframework.data.jpa.repository.Query;
449
- import org.springframework.stereotype.Repository;
450
- import javax.persistence.LockModeType;
451
- import java.util.Optional;
452
-
453
- @Repository
454
- public interface JpaAccountRepository extends JpaRepository<AccountEntity, String>, AccountRepository {
455
-
456
- @Lock(LockModeType.PESSIMISTIC_WRITE)
457
- @Query("SELECT a FROM AccountEntity a WHERE a.id = :id")
458
- Optional<AccountEntity> findByIdForUpdate(String id);
459
-
460
- @Query("SELECT a FROM AccountEntity a WHERE a.customerId = :customerId AND a.status = 'ACTIVE'")
461
- List<AccountEntity> findActiveAccountsByCustomer(String customerId);
462
- }
463
-
464
- // Domain Repository Interface
465
- public interface AccountRepository {
466
- Optional<Account> findById(AccountId id);
467
- Account save(Account account);
468
- void delete(Account account);
469
- List<Account> findByCustomerId(CustomerId customerId);
470
- }
471
-
472
- // Repository Adapter
473
- @Component
474
- public class AccountRepositoryAdapter implements AccountRepository {
475
- private final JpaAccountRepository jpaRepository;
476
- private final AccountMapper mapper;
477
-
478
- @Override
479
- public Optional<Account> findById(AccountId id) {
480
- return jpaRepository.findByIdForUpdate(id.value())
481
- .map(mapper::toDomain);
482
- }
483
-
484
- @Override
485
- public Account save(Account account) {
486
- AccountEntity entity = mapper.toEntity(account);
487
- AccountEntity saved = jpaRepository.save(entity);
488
- return mapper.toDomain(saved);
489
- }
490
- }
491
-
492
- // JPA Entity
493
- @Entity
494
- @Table(name = "accounts")
495
- public class AccountEntity {
496
- @Id
497
- private String id;
498
-
499
- @Column(name = "customer_id", nullable = false)
500
- private String customerId;
501
-
502
- @Enumerated(EnumType.STRING)
503
- @Column(nullable = false)
504
- private AccountType type;
505
-
506
- @Column(nullable = false, precision = 19, scale = 4)
507
- private BigDecimal balance;
508
-
509
- @Enumerated(EnumType.STRING)
510
- @Column(nullable = false)
511
- private AccountStatus status;
512
-
513
- @Column(name = "overdraft_limit", precision = 19, scale = 4)
514
- private BigDecimal overdraftLimit;
515
-
516
- @Column(name = "created_at", nullable = false)
517
- private LocalDateTime createdAt;
518
-
519
- @Column(name = "closed_at")
520
- private LocalDateTime closedAt;
521
-
522
- @Version
523
- private Long version; // Optimistic locking
524
-
525
- // Getters and setters
526
- }
527
- ```
528
-
529
- ### 4. Presentation Layer - REST API
530
-
531
- **Account Controller**
532
-
533
- ```java
534
- // src/main/java/com/bank/presentation/api/AccountController.java
535
- package com.bank.presentation.api;
536
-
537
- import com.bank.application.account.*;
538
- import com.bank.domain.common.Money;
539
- import org.springframework.http.ResponseEntity;
540
- import org.springframework.security.access.prepost.PreAuthorize;
541
- import org.springframework.web.bind.annotation.*;
542
- import javax.validation.Valid;
543
-
544
- @RestController
545
- @RequestMapping("/api/v1/accounts")
546
- public class AccountController {
547
- private final AccountService accountService;
548
- private final TransferService transferService;
549
-
550
- @GetMapping("/{accountId}")
551
- @PreAuthorize("hasPermission(#accountId, 'Account', 'READ')")
552
- public ResponseEntity<AccountDto> getAccount(@PathVariable String accountId) {
553
- Account account = accountService.getAccount(new AccountId(accountId));
554
- return ResponseEntity.ok(AccountDto.from(account));
555
- }
556
-
557
- @GetMapping("/{accountId}/balance")
558
- @PreAuthorize("hasPermission(#accountId, 'Account', 'READ')")
559
- public ResponseEntity<BalanceDto> getBalance(@PathVariable String accountId) {
560
- Money balance = accountService.getBalance(new AccountId(accountId));
561
- return ResponseEntity.ok(new BalanceDto(balance.getAmount(), balance.getCurrency()));
562
- }
563
-
564
- @PostMapping("/{accountId}/deposit")
565
- @PreAuthorize("hasPermission(#accountId, 'Account', 'WRITE')")
566
- public ResponseEntity<TransactionDto> deposit(
567
- @PathVariable String accountId,
568
- @Valid @RequestBody DepositRequest request
569
- ) {
570
- Transaction transaction = accountService.deposit(
571
- new AccountId(accountId),
572
- new Money(request.amount(), Currency.valueOf(request.currency())),
573
- request.description()
574
- );
575
-
576
- return ResponseEntity.ok(TransactionDto.from(transaction));
577
- }
578
-
579
- @PostMapping("/{accountId}/withdraw")
580
- @PreAuthorize("hasPermission(#accountId, 'Account', 'WRITE')")
581
- public ResponseEntity<TransactionDto> withdraw(
582
- @PathVariable String accountId,
583
- @Valid @RequestBody WithdrawRequest request
584
- ) {
585
- Transaction transaction = accountService.withdraw(
586
- new AccountId(accountId),
587
- new Money(request.amount(), Currency.valueOf(request.currency())),
588
- request.description()
589
- );
590
-
591
- return ResponseEntity.ok(TransactionDto.from(transaction));
592
- }
593
-
594
- @PostMapping("/transfer")
595
- @PreAuthorize("hasPermission(#request.sourceAccountId, 'Account', 'WRITE')")
596
- public ResponseEntity<TransferDto> transfer(@Valid @RequestBody TransferRequest request) {
597
- TransferResult result = transferService.executeTransfer(request);
598
- return ResponseEntity.ok(TransferDto.from(result));
599
- }
600
-
601
- @GetMapping("/{accountId}/transactions")
602
- @PreAuthorize("hasPermission(#accountId, 'Account', 'READ')")
603
- public ResponseEntity<Page<TransactionDto>> getTransactions(
604
- @PathVariable String accountId,
605
- @RequestParam(defaultValue = "0") int page,
606
- @RequestParam(defaultValue = "20") int size
607
- ) {
608
- Page<Transaction> transactions = accountService.getTransactions(
609
- new AccountId(accountId),
610
- PageRequest.of(page, size)
611
- );
612
-
613
- return ResponseEntity.ok(transactions.map(TransactionDto::from));
614
- }
615
- }
616
-
617
- // DTOs
618
- public record AccountDto(
619
- String id,
620
- String customerId,
621
- String type,
622
- BigDecimal balance,
623
- String currency,
624
- String status,
625
- LocalDateTime createdAt
626
- ) {
627
- public static AccountDto from(Account account) {
628
- return new AccountDto(
629
- account.getId().value(),
630
- account.getCustomerId().value(),
631
- account.getType().name(),
632
- account.getBalance().getAmount(),
633
- account.getBalance().getCurrency().name(),
634
- account.getStatus().name(),
635
- account.getCreatedAt()
636
- );
637
- }
638
- }
639
-
640
- public record DepositRequest(
641
- @NotNull @Positive BigDecimal amount,
642
- @NotNull String currency,
643
- @NotBlank String description
644
- ) {}
645
-
646
- public record WithdrawRequest(
647
- @NotNull @Positive BigDecimal amount,
648
- @NotNull String currency,
649
- @NotBlank String description
650
- ) {}
651
- ```
652
-
653
- ---
654
-
655
- ## Security Implementation
656
-
657
- ### 1. Authentication & Authorization
658
-
659
- **Spring Security Configuration**
660
-
661
- ```java
662
- // src/main/java/com/bank/infrastructure/security/SecurityConfig.java
663
- package com.bank.infrastructure.security;
664
-
665
- import org.springframework.context.annotation.Bean;
666
- import org.springframework.context.annotation.Configuration;
667
- import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
668
- import org.springframework.security.config.annotation.web.builders.HttpSecurity;
669
- import org.springframework.security.config.http.SessionCreationPolicy;
670
- import org.springframework.security.web.SecurityFilterChain;
671
-
672
- @Configuration
673
- @EnableMethodSecurity(prePostEnabled = true)
674
- public class SecurityConfig {
675
-
676
- @Bean
677
- public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
678
- http
679
- .csrf().disable()
680
- .sessionManagement()
681
- .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
682
- .and()
683
- .authorizeHttpRequests(auth -> auth
684
- .requestMatchers("/api/v1/public/**").permitAll()
685
- .requestMatchers("/api/v1/accounts/**").authenticated()
686
- .requestMatchers("/api/v1/admin/**").hasRole("ADMIN")
687
- .anyRequest().authenticated()
688
- )
689
- .oauth2ResourceServer()
690
- .jwt()
691
- .jwtAuthenticationConverter(jwtAuthenticationConverter());
692
-
693
- return http.build();
694
- }
695
-
696
- @Bean
697
- public JwtAuthenticationConverter jwtAuthenticationConverter() {
698
- JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
699
- grantedAuthoritiesConverter.setAuthoritiesClaimName("roles");
700
- grantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");
701
-
702
- JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
703
- jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
704
- return jwtAuthenticationConverter;
705
- }
706
- }
707
- ```
708
-
709
- **Custom Permission Evaluator**
710
-
711
- ```java
712
- // src/main/java/com/bank/infrastructure/security/AccountPermissionEvaluator.java
713
- @Component
714
- public class AccountPermissionEvaluator implements PermissionEvaluator {
715
- private final AccountRepository accountRepository;
716
-
717
- @Override
718
- public boolean hasPermission(
719
- Authentication authentication,
720
- Object targetDomainObject,
721
- Object permission
722
- ) {
723
- if (authentication == null || !(targetDomainObject instanceof String)) {
724
- return false;
725
- }
726
-
727
- String accountId = (String) targetDomainObject;
728
- String userId = authentication.getName();
729
-
730
- // Check if user owns the account
731
- Account account = accountRepository.findById(new AccountId(accountId))
732
- .orElseThrow(() -> new AccountNotFoundException(accountId));
733
-
734
- return account.getCustomerId().value().equals(userId) ||
735
- authentication.getAuthorities().stream()
736
- .anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"));
737
- }
738
- }
739
- ```
740
-
741
-
742
- ### 2. Audit Logging
743
-
744
- **Comprehensive Audit Trail**
745
-
746
- ```java
747
- // src/main/java/com/bank/infrastructure/audit/AuditLogger.java
748
- @Component
749
- public class AuditLogger {
750
- private final AuditRepository auditRepository;
751
- private final KafkaTemplate<String, AuditEvent> kafkaTemplate;
752
-
753
- public void logTransfer(TransferRequest request, Transfer transfer, String status) {
754
- AuditEvent event = AuditEvent.builder()
755
- .eventType("TRANSFER")
756
- .userId(SecurityContextHolder.getContext().getAuthentication().getName())
757
- .entityType("Transfer")
758
- .entityId(transfer.getId().value())
759
- .action("EXECUTE")
760
- .status(status)
761
- .details(Map.of(
762
- "sourceAccountId", request.sourceAccountId(),
763
- "targetAccountId", request.targetAccountId(),
764
- "amount", request.amount().toString(),
765
- "description", request.description()
766
- ))
767
- .ipAddress(getClientIpAddress())
768
- .timestamp(LocalDateTime.now())
769
- .build();
770
-
771
- // Store in database
772
- auditRepository.save(event);
773
-
774
- // Publish to Kafka for real-time monitoring
775
- kafkaTemplate.send("audit-events", event);
776
- }
777
-
778
- public void logSuspiciousActivity(TransferRequest request, FraudCheckResult fraudCheck) {
779
- AuditEvent event = AuditEvent.builder()
780
- .eventType("FRAUD_ALERT")
781
- .userId(SecurityContextHolder.getContext().getAuthentication().getName())
782
- .action("TRANSFER_BLOCKED")
783
- .status("SUSPICIOUS")
784
- .details(Map.of(
785
- "reason", fraudCheck.getReason(),
786
- "riskScore", fraudCheck.getRiskScore(),
787
- "amount", request.amount().toString()
788
- ))
789
- .timestamp(LocalDateTime.now())
790
- .build();
791
-
792
- auditRepository.save(event);
793
- kafkaTemplate.send("fraud-alerts", event);
794
- }
795
- }
796
- ```
797
-
798
- ### 3. Data Encryption
799
-
800
- **Encryption at Rest and in Transit**
801
-
802
- ```java
803
- // src/main/java/com/bank/infrastructure/security/EncryptionService.java
804
- @Service
805
- public class EncryptionService {
806
- private final KeyManagementService kms;
807
-
808
- public String encryptSensitiveData(String plaintext) {
809
- try {
810
- // Use AWS KMS or HSM for key management
811
- byte[] dataKey = kms.generateDataKey();
812
-
813
- Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
814
- GCMParameterSpec spec = new GCMParameterSpec(128, generateIV());
815
- SecretKeySpec keySpec = new SecretKeySpec(dataKey, "AES");
816
-
817
- cipher.init(Cipher.ENCRYPT_MODE, keySpec, spec);
818
- byte[] encrypted = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
819
-
820
- return Base64.getEncoder().encodeToString(encrypted);
821
- } catch (Exception e) {
822
- throw new EncryptionException("Failed to encrypt data", e);
823
- }
824
- }
825
-
826
- public String decryptSensitiveData(String ciphertext) {
827
- // Decryption logic
828
- }
829
- }
830
- ```
831
-
832
- ---
833
-
834
- ## Compliance & Regulatory
835
-
836
- ### 1. SOX Compliance
837
-
838
- **Segregation of Duties**
839
-
840
- ```java
841
- // src/main/java/com/bank/infrastructure/compliance/SoxComplianceService.java
842
- @Service
843
- public class SoxComplianceService {
844
-
845
- @PreAuthorize("hasRole('MAKER')")
846
- public void createTransaction(TransactionRequest request) {
847
- // Maker creates transaction in PENDING state
848
- Transaction transaction = Transaction.createPending(request);
849
- transactionRepository.save(transaction);
850
- }
851
-
852
- @PreAuthorize("hasRole('CHECKER') and !hasRole('MAKER')")
853
- public void approveTransaction(String transactionId) {
854
- // Checker approves (different person than maker)
855
- Transaction transaction = transactionRepository.findById(transactionId);
856
-
857
- if (transaction.getCreatedBy().equals(getCurrentUser())) {
858
- throw new SoxViolationException("Cannot approve own transaction");
859
- }
860
-
861
- transaction.approve(getCurrentUser());
862
- transactionRepository.save(transaction);
863
- }
864
- }
865
- ```
866
-
867
- ### 2. GDPR Compliance
868
-
869
- **Data Privacy and Right to be Forgotten**
870
-
871
- ```java
872
- // src/main/java/com/bank/application/gdpr/GdprService.java
873
- @Service
874
- public class GdprService {
875
-
876
- public CustomerDataExport exportCustomerData(CustomerId customerId) {
877
- // Export all customer data
878
- Customer customer = customerRepository.findById(customerId);
879
- List<Account> accounts = accountRepository.findByCustomerId(customerId);
880
- List<Transaction> transactions = transactionRepository.findByCustomerId(customerId);
881
-
882
- return CustomerDataExport.builder()
883
- .customer(customer)
884
- .accounts(accounts)
885
- .transactions(transactions)
886
- .exportDate(LocalDateTime.now())
887
- .build();
888
- }
889
-
890
- @Transactional
891
- public void anonymizeCustomerData(CustomerId customerId) {
892
- // Anonymize customer data (cannot delete due to regulatory requirements)
893
- Customer customer = customerRepository.findById(customerId);
894
- customer.anonymize(); // Replace PII with anonymized values
895
-
896
- customerRepository.save(customer);
897
-
898
- auditLogger.log("GDPR_ANONYMIZATION", customerId);
899
- }
900
- }
901
- ```
902
-
903
- ---
904
-
905
- ## Key Takeaways
906
-
907
- ### Architecture Decisions
908
-
909
- 1. **Layered Architecture**: Clear separation of concerns, easier to understand and maintain
910
- 2. **Rich Domain Model**: Business logic in domain layer, not anemic models
911
- 3. **Repository Pattern**: Abstracts data access, enables testing
912
- 4. **ACID Transactions**: PostgreSQL ensures data integrity
913
- 5. **Pessimistic Locking**: Prevents concurrent modification of accounts
914
- 6. **Audit Logging**: Complete trail for compliance
915
- 7. **Encryption**: AES-256 for sensitive data
916
-
917
- ### Trade-offs
918
-
919
- **Benefits**
920
- - ✅ Clear separation of concerns
921
- - ✅ Easy to understand and maintain
922
- - ✅ Strong data consistency (ACID)
923
- - ✅ Comprehensive security
924
- - ✅ Regulatory compliance
925
- - ✅ Testable layers
926
-
927
- **Challenges**
928
- - ❌ Monolithic deployment (all layers together)
929
- - ❌ Vertical scaling only
930
- - ❌ Technology lock-in (Java/Spring)
931
- - ❌ Layer overhead for simple operations
932
- - ❌ Tight coupling within monolith
933
-
934
- ### Security Measures
935
-
936
- - **Authentication**: OAuth 2.0, JWT tokens
937
- - **Authorization**: Role-based access control (RBAC)
938
- - **Encryption**: AES-256 at rest, TLS 1.3 in transit
939
- - **MFA**: TOTP, SMS, biometric
940
- - **Audit**: Complete audit trail
941
- - **Compliance**: SOX, PCI DSS, GDPR, Basel III
942
-
943
- ### Performance Metrics
944
-
945
- - **Transaction Processing**: < 500ms (P95)
946
- - **API Response Time**: < 200ms (P95)
947
- - **Database Queries**: < 100ms (P95)
948
- - **Availability**: 99.95%
949
- - **Throughput**: 100,000 transactions/day
950
-
951
- ---
952
-
953
- ## References
954
-
955
- - [Layered Architecture](../rules/layered-architecture.md)
956
- - [Security Architecture](../rules/security.md)
957
- - [Domain-Driven Design](../rules/tools-methodologies.md)
958
- - [Quality Attributes](../rules/quality-attributes.md)
959
- - [Spring Security Documentation](https://spring.io/projects/spring-security)
960
- - [PCI DSS Compliance](https://www.pcisecuritystandards.org/)
961
-
1
+ # Banking Application Architecture Example
2
+
3
+ ## Overview
4
+
5
+ This document provides a comprehensive example of a banking application built with layered architecture, focusing on security, compliance, and data integrity.
6
+
7
+ ---
8
+
9
+ ## System Context
10
+
11
+ ### Business Requirements
12
+
13
+ **Functional Requirements**
14
+ - Account management (checking, savings, credit)
15
+ - Fund transfers (internal and external)
16
+ - Transaction history and statements
17
+ - Bill payments and recurring payments
18
+ - Loan applications and management
19
+ - Customer authentication and authorization
20
+ - Fraud detection and alerts
21
+ - Regulatory reporting
22
+
23
+ **Non-Functional Requirements**
24
+ - **Security**: Multi-factor authentication, encryption at rest and in transit
25
+ - **Compliance**: SOX, PCI DSS, GDPR, Basel III
26
+ - **Availability**: 99.95% uptime (4.38 hours downtime/year)
27
+ - **Data Integrity**: ACID transactions, zero data loss
28
+ - **Auditability**: Complete audit trail for all transactions
29
+ - **Performance**: Transaction processing < 500ms
30
+
31
+ ### Scale Metrics
32
+ - 500,000 active customers
33
+ - 1 million accounts
34
+ - 100,000 transactions per day
35
+ - $10 billion in assets under management
36
+ - 24/7 operation with global presence
37
+
38
+ ---
39
+
40
+ ## Architecture Overview
41
+
42
+ ### Layered Architecture Pattern
43
+
44
+ **Four-Tier Architecture**
45
+
46
+ ```
47
+ ┌─────────────────────────────────────────────────────────────┐
48
+ │ Presentation Layer │
49
+ │ Web UI, Mobile Apps, ATM Interface, APIs │
50
+ └─────────────────────────────────────────────────────────────┘
51
+
52
+ ┌─────────────────────────────────────────────────────────────┐
53
+ │ Application Layer │
54
+ │ Use Cases, Orchestration, Transaction Coordination │
55
+ │ (Account Service, Transfer Service, Loan Service) │
56
+ └─────────────────────────────────────────────────────────────┘
57
+
58
+ ┌─────────────────────────────────────────────────────────────┐
59
+ │ Domain Layer │
60
+ │ Business Logic, Domain Models, Business Rules │
61
+ │ (Account, Transaction, Customer, Loan) │
62
+ └─────────────────────────────────────────────────────────────┘
63
+
64
+ ┌─────────────────────────────────────────────────────────────┐
65
+ │ Infrastructure Layer │
66
+ │ Database, External Services, Messaging, Security │
67
+ │ (PostgreSQL, Redis, Kafka, SWIFT, ACH) │
68
+ └─────────────────────────────────────────────────────────────┘
69
+ ```
70
+
71
+ ### Layer Responsibilities
72
+
73
+ **1. Presentation Layer**
74
+ - Web application (React)
75
+ - Mobile apps (iOS, Android)
76
+ - ATM interface
77
+ - Admin portal
78
+ - RESTful APIs
79
+ - Input validation
80
+ - Session management
81
+
82
+ **2. Application Layer**
83
+ - Account management use cases
84
+ - Transfer orchestration
85
+ - Loan processing workflows
86
+ - Transaction coordination
87
+ - Event publishing
88
+ - External service integration
89
+
90
+ **3. Domain Layer**
91
+ - Account entity and business rules
92
+ - Transaction processing logic
93
+ - Interest calculation
94
+ - Overdraft protection
95
+ - Fraud detection rules
96
+ - Loan approval logic
97
+
98
+ **4. Infrastructure Layer**
99
+ - PostgreSQL database (ACID compliance)
100
+ - Redis cache (session, rate limiting)
101
+ - Kafka message broker (event streaming)
102
+ - SWIFT integration (international transfers)
103
+ - ACH integration (domestic transfers)
104
+ - HSM (Hardware Security Module) for encryption
105
+
106
+ ---
107
+
108
+ ## Technology Stack
109
+
110
+ ### Presentation Layer
111
+ - **Web**: React, TypeScript, Material-UI
112
+ - **Mobile**: React Native
113
+ - **API**: Node.js, Express, GraphQL
114
+
115
+ ### Application Layer
116
+ - **Language**: Java 17
117
+ - **Framework**: Spring Boot 3.x
118
+ - **Security**: Spring Security, OAuth 2.0
119
+
120
+ ### Domain Layer
121
+ - **Language**: Java 17
122
+ - **Patterns**: Domain-Driven Design (DDD)
123
+ - **Validation**: Bean Validation (JSR 380)
124
+
125
+ ### Infrastructure Layer
126
+ - **Database**: PostgreSQL 15 (primary), PostgreSQL replicas (read)
127
+ - **Cache**: Redis Cluster
128
+ - **Message Broker**: Apache Kafka
129
+ - **Search**: Elasticsearch (transaction search)
130
+ - **File Storage**: AWS S3 (statements, documents)
131
+
132
+ ### Security & Compliance
133
+ - **Authentication**: OAuth 2.0, OpenID Connect
134
+ - **MFA**: TOTP, SMS, Biometric
135
+ - **Encryption**: AES-256 (at rest), TLS 1.3 (in transit)
136
+ - **HSM**: Thales Luna HSM
137
+ - **Audit**: Splunk, custom audit tables
138
+
139
+ ### Infrastructure
140
+ - **Hosting**: AWS (multi-region)
141
+ - **Container**: Docker, Kubernetes (EKS)
142
+ - **Load Balancer**: AWS ALB
143
+ - **CDN**: CloudFront
144
+ - **Monitoring**: Datadog, CloudWatch
145
+
146
+ ---
147
+
148
+ ## Implementation Details
149
+
150
+ ### 1. Domain Layer - Account Entity
151
+
152
+ **Rich Domain Model**
153
+
154
+ ```java
155
+ // src/main/java/com/bank/domain/account/Account.java
156
+ package com.bank.domain.account;
157
+
158
+ import com.bank.domain.common.Money;
159
+ import com.bank.domain.transaction.Transaction;
160
+ import javax.validation.constraints.*;
161
+ import java.math.BigDecimal;
162
+ import java.time.LocalDateTime;
163
+ import java.util.ArrayList;
164
+ import java.util.List;
165
+
166
+ public class Account {
167
+ @NotNull
168
+ private final AccountId id;
169
+
170
+ @NotNull
171
+ private final CustomerId customerId;
172
+
173
+ @NotNull
174
+ private final AccountType type;
175
+
176
+ @NotNull
177
+ private Money balance;
178
+
179
+ @NotNull
180
+ private AccountStatus status;
181
+
182
+ private Money overdraftLimit;
183
+
184
+ @NotNull
185
+ private final LocalDateTime createdAt;
186
+
187
+ private LocalDateTime closedAt;
188
+
189
+ private final List<Transaction> transactions = new ArrayList<>();
190
+
191
+ // Business logic methods
192
+ public void deposit(Money amount, String description) {
193
+ validateAccountActive();
194
+ validatePositiveAmount(amount);
195
+
196
+ this.balance = this.balance.add(amount);
197
+
198
+ Transaction transaction = Transaction.createDeposit(
199
+ this.id,
200
+ amount,
201
+ description,
202
+ LocalDateTime.now()
203
+ );
204
+
205
+ this.transactions.add(transaction);
206
+ }
207
+
208
+ public void withdraw(Money amount, String description) {
209
+ validateAccountActive();
210
+ validatePositiveAmount(amount);
211
+ validateSufficientFunds(amount);
212
+
213
+ this.balance = this.balance.subtract(amount);
214
+
215
+ Transaction transaction = Transaction.createWithdrawal(
216
+ this.id,
217
+ amount,
218
+ description,
219
+ LocalDateTime.now()
220
+ );
221
+
222
+ this.transactions.add(transaction);
223
+ }
224
+
225
+ public void transfer(Account targetAccount, Money amount, String description) {
226
+ validateAccountActive();
227
+ targetAccount.validateAccountActive();
228
+ validatePositiveAmount(amount);
229
+ validateSufficientFunds(amount);
230
+ validateDifferentAccounts(targetAccount);
231
+
232
+ this.withdraw(amount, "Transfer to " + targetAccount.getId());
233
+ targetAccount.deposit(amount, "Transfer from " + this.getId());
234
+ }
235
+
236
+ public Money calculateInterest(BigDecimal annualRate, int days) {
237
+ if (this.type != AccountType.SAVINGS) {
238
+ throw new IllegalStateException("Interest only applies to savings accounts");
239
+ }
240
+
241
+ BigDecimal dailyRate = annualRate.divide(BigDecimal.valueOf(365), 10, BigDecimal.ROUND_HALF_UP);
242
+ BigDecimal interest = this.balance.getAmount()
243
+ .multiply(dailyRate)
244
+ .multiply(BigDecimal.valueOf(days));
245
+
246
+ return new Money(interest, this.balance.getCurrency());
247
+ }
248
+
249
+ // Validation methods
250
+ private void validateAccountActive() {
251
+ if (this.status != AccountStatus.ACTIVE) {
252
+ throw new AccountNotActiveException("Account " + this.id + " is not active");
253
+ }
254
+ }
255
+
256
+ private void validatePositiveAmount(Money amount) {
257
+ if (amount.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
258
+ throw new IllegalArgumentException("Amount must be positive");
259
+ }
260
+ }
261
+
262
+ private void validateSufficientFunds(Money amount) {
263
+ Money availableBalance = this.balance.add(this.overdraftLimit != null ? this.overdraftLimit : Money.ZERO);
264
+
265
+ if (availableBalance.compareTo(amount) < 0) {
266
+ throw new InsufficientFundsException(
267
+ "Insufficient funds. Available: " + availableBalance + ", Required: " + amount
268
+ );
269
+ }
270
+ }
271
+
272
+ private void validateDifferentAccounts(Account other) {
273
+ if (this.id.equals(other.id)) {
274
+ throw new IllegalArgumentException("Cannot transfer to the same account");
275
+ }
276
+ }
277
+
278
+ // Getters
279
+ public AccountId getId() { return id; }
280
+ public Money getBalance() { return balance; }
281
+ public AccountStatus getStatus() { return status; }
282
+ }
283
+
284
+ // Value Objects
285
+ public record AccountId(String value) {
286
+ public AccountId {
287
+ if (value == null || value.isBlank()) {
288
+ throw new IllegalArgumentException("Account ID cannot be null or blank");
289
+ }
290
+ }
291
+ }
292
+
293
+ public record Money(BigDecimal amount, Currency currency) implements Comparable<Money> {
294
+ public static final Money ZERO = new Money(BigDecimal.ZERO, Currency.USD);
295
+
296
+ public Money add(Money other) {
297
+ validateSameCurrency(other);
298
+ return new Money(this.amount.add(other.amount), this.currency);
299
+ }
300
+
301
+ public Money subtract(Money other) {
302
+ validateSameCurrency(other);
303
+ return new Money(this.amount.subtract(other.amount), this.currency);
304
+ }
305
+
306
+ @Override
307
+ public int compareTo(Money other) {
308
+ validateSameCurrency(other);
309
+ return this.amount.compareTo(other.amount);
310
+ }
311
+
312
+ private void validateSameCurrency(Money other) {
313
+ if (!this.currency.equals(other.currency)) {
314
+ throw new IllegalArgumentException("Cannot operate on different currencies");
315
+ }
316
+ }
317
+ }
318
+
319
+ public enum AccountType {
320
+ CHECKING, SAVINGS, CREDIT, LOAN
321
+ }
322
+
323
+ public enum AccountStatus {
324
+ ACTIVE, SUSPENDED, CLOSED, FROZEN
325
+ }
326
+ ```
327
+
328
+ ### 2. Application Layer - Transfer Service
329
+
330
+ **Use Case Orchestration**
331
+
332
+ ```java
333
+ // src/main/java/com/bank/application/transfer/TransferService.java
334
+ package com.bank.application.transfer;
335
+
336
+ import com.bank.domain.account.*;
337
+ import com.bank.domain.common.Money;
338
+ import com.bank.infrastructure.events.EventPublisher;
339
+ import com.bank.infrastructure.fraud.FraudDetectionService;
340
+ import org.springframework.stereotype.Service;
341
+ import org.springframework.transaction.annotation.Transactional;
342
+ import java.time.LocalDateTime;
343
+
344
+ @Service
345
+ public class TransferService {
346
+ private final AccountRepository accountRepository;
347
+ private final TransferRepository transferRepository;
348
+ private final FraudDetectionService fraudDetection;
349
+ private final EventPublisher eventPublisher;
350
+ private final AuditLogger auditLogger;
351
+
352
+ public TransferService(
353
+ AccountRepository accountRepository,
354
+ TransferRepository transferRepository,
355
+ FraudDetectionService fraudDetection,
356
+ EventPublisher eventPublisher,
357
+ AuditLogger auditLogger
358
+ ) {
359
+ this.accountRepository = accountRepository;
360
+ this.transferRepository = transferRepository;
361
+ this.fraudDetection = fraudDetection;
362
+ this.eventPublisher = eventPublisher;
363
+ this.auditLogger = auditLogger;
364
+ }
365
+
366
+ @Transactional
367
+ public TransferResult executeTransfer(TransferRequest request) {
368
+ // 1. Validate request
369
+ validateTransferRequest(request);
370
+
371
+ // 2. Load accounts
372
+ Account sourceAccount = accountRepository.findById(request.sourceAccountId())
373
+ .orElseThrow(() -> new AccountNotFoundException(request.sourceAccountId()));
374
+
375
+ Account targetAccount = accountRepository.findById(request.targetAccountId())
376
+ .orElseThrow(() -> new AccountNotFoundException(request.targetAccountId()));
377
+
378
+ // 3. Fraud detection
379
+ FraudCheckResult fraudCheck = fraudDetection.checkTransfer(
380
+ sourceAccount,
381
+ targetAccount,
382
+ request.amount()
383
+ );
384
+
385
+ if (fraudCheck.isSuspicious()) {
386
+ auditLogger.logSuspiciousActivity(request, fraudCheck);
387
+ throw new SuspiciousActivityException("Transfer flagged for review");
388
+ }
389
+
390
+ // 4. Execute transfer (domain logic)
391
+ sourceAccount.transfer(targetAccount, request.amount(), request.description());
392
+
393
+ // 5. Save accounts
394
+ accountRepository.save(sourceAccount);
395
+ accountRepository.save(targetAccount);
396
+
397
+ // 6. Create transfer record
398
+ Transfer transfer = Transfer.create(
399
+ sourceAccount.getId(),
400
+ targetAccount.getId(),
401
+ request.amount(),
402
+ request.description(),
403
+ TransferStatus.COMPLETED,
404
+ LocalDateTime.now()
405
+ );
406
+ transferRepository.save(transfer);
407
+
408
+ // 7. Publish event
409
+ eventPublisher.publish(new TransferCompletedEvent(
410
+ transfer.getId(),
411
+ sourceAccount.getId(),
412
+ targetAccount.getId(),
413
+ request.amount(),
414
+ LocalDateTime.now()
415
+ ));
416
+
417
+ // 8. Audit log
418
+ auditLogger.logTransfer(request, transfer, "SUCCESS");
419
+
420
+ return new TransferResult(transfer.getId(), TransferStatus.COMPLETED);
421
+ }
422
+
423
+ private void validateTransferRequest(TransferRequest request) {
424
+ if (request.amount().getAmount().compareTo(BigDecimal.ZERO) <= 0) {
425
+ throw new IllegalArgumentException("Transfer amount must be positive");
426
+ }
427
+
428
+ if (request.sourceAccountId().equals(request.targetAccountId())) {
429
+ throw new IllegalArgumentException("Source and target accounts must be different");
430
+ }
431
+ }
432
+ }
433
+ ```
434
+
435
+
436
+
437
+ ### 3. Infrastructure Layer - Repository Implementation
438
+
439
+ **Data Access with JPA**
440
+
441
+ ```java
442
+ // src/main/java/com/bank/infrastructure/persistence/JpaAccountRepository.java
443
+ package com.bank.infrastructure.persistence;
444
+
445
+ import com.bank.domain.account.*;
446
+ import org.springframework.data.jpa.repository.JpaRepository;
447
+ import org.springframework.data.jpa.repository.Lock;
448
+ import org.springframework.data.jpa.repository.Query;
449
+ import org.springframework.stereotype.Repository;
450
+ import javax.persistence.LockModeType;
451
+ import java.util.Optional;
452
+
453
+ @Repository
454
+ public interface JpaAccountRepository extends JpaRepository<AccountEntity, String>, AccountRepository {
455
+
456
+ @Lock(LockModeType.PESSIMISTIC_WRITE)
457
+ @Query("SELECT a FROM AccountEntity a WHERE a.id = :id")
458
+ Optional<AccountEntity> findByIdForUpdate(String id);
459
+
460
+ @Query("SELECT a FROM AccountEntity a WHERE a.customerId = :customerId AND a.status = 'ACTIVE'")
461
+ List<AccountEntity> findActiveAccountsByCustomer(String customerId);
462
+ }
463
+
464
+ // Domain Repository Interface
465
+ public interface AccountRepository {
466
+ Optional<Account> findById(AccountId id);
467
+ Account save(Account account);
468
+ void delete(Account account);
469
+ List<Account> findByCustomerId(CustomerId customerId);
470
+ }
471
+
472
+ // Repository Adapter
473
+ @Component
474
+ public class AccountRepositoryAdapter implements AccountRepository {
475
+ private final JpaAccountRepository jpaRepository;
476
+ private final AccountMapper mapper;
477
+
478
+ @Override
479
+ public Optional<Account> findById(AccountId id) {
480
+ return jpaRepository.findByIdForUpdate(id.value())
481
+ .map(mapper::toDomain);
482
+ }
483
+
484
+ @Override
485
+ public Account save(Account account) {
486
+ AccountEntity entity = mapper.toEntity(account);
487
+ AccountEntity saved = jpaRepository.save(entity);
488
+ return mapper.toDomain(saved);
489
+ }
490
+ }
491
+
492
+ // JPA Entity
493
+ @Entity
494
+ @Table(name = "accounts")
495
+ public class AccountEntity {
496
+ @Id
497
+ private String id;
498
+
499
+ @Column(name = "customer_id", nullable = false)
500
+ private String customerId;
501
+
502
+ @Enumerated(EnumType.STRING)
503
+ @Column(nullable = false)
504
+ private AccountType type;
505
+
506
+ @Column(nullable = false, precision = 19, scale = 4)
507
+ private BigDecimal balance;
508
+
509
+ @Enumerated(EnumType.STRING)
510
+ @Column(nullable = false)
511
+ private AccountStatus status;
512
+
513
+ @Column(name = "overdraft_limit", precision = 19, scale = 4)
514
+ private BigDecimal overdraftLimit;
515
+
516
+ @Column(name = "created_at", nullable = false)
517
+ private LocalDateTime createdAt;
518
+
519
+ @Column(name = "closed_at")
520
+ private LocalDateTime closedAt;
521
+
522
+ @Version
523
+ private Long version; // Optimistic locking
524
+
525
+ // Getters and setters
526
+ }
527
+ ```
528
+
529
+ ### 4. Presentation Layer - REST API
530
+
531
+ **Account Controller**
532
+
533
+ ```java
534
+ // src/main/java/com/bank/presentation/api/AccountController.java
535
+ package com.bank.presentation.api;
536
+
537
+ import com.bank.application.account.*;
538
+ import com.bank.domain.common.Money;
539
+ import org.springframework.http.ResponseEntity;
540
+ import org.springframework.security.access.prepost.PreAuthorize;
541
+ import org.springframework.web.bind.annotation.*;
542
+ import javax.validation.Valid;
543
+
544
+ @RestController
545
+ @RequestMapping("/api/v1/accounts")
546
+ public class AccountController {
547
+ private final AccountService accountService;
548
+ private final TransferService transferService;
549
+
550
+ @GetMapping("/{accountId}")
551
+ @PreAuthorize("hasPermission(#accountId, 'Account', 'READ')")
552
+ public ResponseEntity<AccountDto> getAccount(@PathVariable String accountId) {
553
+ Account account = accountService.getAccount(new AccountId(accountId));
554
+ return ResponseEntity.ok(AccountDto.from(account));
555
+ }
556
+
557
+ @GetMapping("/{accountId}/balance")
558
+ @PreAuthorize("hasPermission(#accountId, 'Account', 'READ')")
559
+ public ResponseEntity<BalanceDto> getBalance(@PathVariable String accountId) {
560
+ Money balance = accountService.getBalance(new AccountId(accountId));
561
+ return ResponseEntity.ok(new BalanceDto(balance.getAmount(), balance.getCurrency()));
562
+ }
563
+
564
+ @PostMapping("/{accountId}/deposit")
565
+ @PreAuthorize("hasPermission(#accountId, 'Account', 'WRITE')")
566
+ public ResponseEntity<TransactionDto> deposit(
567
+ @PathVariable String accountId,
568
+ @Valid @RequestBody DepositRequest request
569
+ ) {
570
+ Transaction transaction = accountService.deposit(
571
+ new AccountId(accountId),
572
+ new Money(request.amount(), Currency.valueOf(request.currency())),
573
+ request.description()
574
+ );
575
+
576
+ return ResponseEntity.ok(TransactionDto.from(transaction));
577
+ }
578
+
579
+ @PostMapping("/{accountId}/withdraw")
580
+ @PreAuthorize("hasPermission(#accountId, 'Account', 'WRITE')")
581
+ public ResponseEntity<TransactionDto> withdraw(
582
+ @PathVariable String accountId,
583
+ @Valid @RequestBody WithdrawRequest request
584
+ ) {
585
+ Transaction transaction = accountService.withdraw(
586
+ new AccountId(accountId),
587
+ new Money(request.amount(), Currency.valueOf(request.currency())),
588
+ request.description()
589
+ );
590
+
591
+ return ResponseEntity.ok(TransactionDto.from(transaction));
592
+ }
593
+
594
+ @PostMapping("/transfer")
595
+ @PreAuthorize("hasPermission(#request.sourceAccountId, 'Account', 'WRITE')")
596
+ public ResponseEntity<TransferDto> transfer(@Valid @RequestBody TransferRequest request) {
597
+ TransferResult result = transferService.executeTransfer(request);
598
+ return ResponseEntity.ok(TransferDto.from(result));
599
+ }
600
+
601
+ @GetMapping("/{accountId}/transactions")
602
+ @PreAuthorize("hasPermission(#accountId, 'Account', 'READ')")
603
+ public ResponseEntity<Page<TransactionDto>> getTransactions(
604
+ @PathVariable String accountId,
605
+ @RequestParam(defaultValue = "0") int page,
606
+ @RequestParam(defaultValue = "20") int size
607
+ ) {
608
+ Page<Transaction> transactions = accountService.getTransactions(
609
+ new AccountId(accountId),
610
+ PageRequest.of(page, size)
611
+ );
612
+
613
+ return ResponseEntity.ok(transactions.map(TransactionDto::from));
614
+ }
615
+ }
616
+
617
+ // DTOs
618
+ public record AccountDto(
619
+ String id,
620
+ String customerId,
621
+ String type,
622
+ BigDecimal balance,
623
+ String currency,
624
+ String status,
625
+ LocalDateTime createdAt
626
+ ) {
627
+ public static AccountDto from(Account account) {
628
+ return new AccountDto(
629
+ account.getId().value(),
630
+ account.getCustomerId().value(),
631
+ account.getType().name(),
632
+ account.getBalance().getAmount(),
633
+ account.getBalance().getCurrency().name(),
634
+ account.getStatus().name(),
635
+ account.getCreatedAt()
636
+ );
637
+ }
638
+ }
639
+
640
+ public record DepositRequest(
641
+ @NotNull @Positive BigDecimal amount,
642
+ @NotNull String currency,
643
+ @NotBlank String description
644
+ ) {}
645
+
646
+ public record WithdrawRequest(
647
+ @NotNull @Positive BigDecimal amount,
648
+ @NotNull String currency,
649
+ @NotBlank String description
650
+ ) {}
651
+ ```
652
+
653
+ ---
654
+
655
+ ## Security Implementation
656
+
657
+ ### 1. Authentication & Authorization
658
+
659
+ **Spring Security Configuration**
660
+
661
+ ```java
662
+ // src/main/java/com/bank/infrastructure/security/SecurityConfig.java
663
+ package com.bank.infrastructure.security;
664
+
665
+ import org.springframework.context.annotation.Bean;
666
+ import org.springframework.context.annotation.Configuration;
667
+ import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
668
+ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
669
+ import org.springframework.security.config.http.SessionCreationPolicy;
670
+ import org.springframework.security.web.SecurityFilterChain;
671
+
672
+ @Configuration
673
+ @EnableMethodSecurity(prePostEnabled = true)
674
+ public class SecurityConfig {
675
+
676
+ @Bean
677
+ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
678
+ http
679
+ .csrf().disable()
680
+ .sessionManagement()
681
+ .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
682
+ .and()
683
+ .authorizeHttpRequests(auth -> auth
684
+ .requestMatchers("/api/v1/public/**").permitAll()
685
+ .requestMatchers("/api/v1/accounts/**").authenticated()
686
+ .requestMatchers("/api/v1/admin/**").hasRole("ADMIN")
687
+ .anyRequest().authenticated()
688
+ )
689
+ .oauth2ResourceServer()
690
+ .jwt()
691
+ .jwtAuthenticationConverter(jwtAuthenticationConverter());
692
+
693
+ return http.build();
694
+ }
695
+
696
+ @Bean
697
+ public JwtAuthenticationConverter jwtAuthenticationConverter() {
698
+ JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
699
+ grantedAuthoritiesConverter.setAuthoritiesClaimName("roles");
700
+ grantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");
701
+
702
+ JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
703
+ jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
704
+ return jwtAuthenticationConverter;
705
+ }
706
+ }
707
+ ```
708
+
709
+ **Custom Permission Evaluator**
710
+
711
+ ```java
712
+ // src/main/java/com/bank/infrastructure/security/AccountPermissionEvaluator.java
713
+ @Component
714
+ public class AccountPermissionEvaluator implements PermissionEvaluator {
715
+ private final AccountRepository accountRepository;
716
+
717
+ @Override
718
+ public boolean hasPermission(
719
+ Authentication authentication,
720
+ Object targetDomainObject,
721
+ Object permission
722
+ ) {
723
+ if (authentication == null || !(targetDomainObject instanceof String)) {
724
+ return false;
725
+ }
726
+
727
+ String accountId = (String) targetDomainObject;
728
+ String userId = authentication.getName();
729
+
730
+ // Check if user owns the account
731
+ Account account = accountRepository.findById(new AccountId(accountId))
732
+ .orElseThrow(() -> new AccountNotFoundException(accountId));
733
+
734
+ return account.getCustomerId().value().equals(userId) ||
735
+ authentication.getAuthorities().stream()
736
+ .anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"));
737
+ }
738
+ }
739
+ ```
740
+
741
+
742
+ ### 2. Audit Logging
743
+
744
+ **Comprehensive Audit Trail**
745
+
746
+ ```java
747
+ // src/main/java/com/bank/infrastructure/audit/AuditLogger.java
748
+ @Component
749
+ public class AuditLogger {
750
+ private final AuditRepository auditRepository;
751
+ private final KafkaTemplate<String, AuditEvent> kafkaTemplate;
752
+
753
+ public void logTransfer(TransferRequest request, Transfer transfer, String status) {
754
+ AuditEvent event = AuditEvent.builder()
755
+ .eventType("TRANSFER")
756
+ .userId(SecurityContextHolder.getContext().getAuthentication().getName())
757
+ .entityType("Transfer")
758
+ .entityId(transfer.getId().value())
759
+ .action("EXECUTE")
760
+ .status(status)
761
+ .details(Map.of(
762
+ "sourceAccountId", request.sourceAccountId(),
763
+ "targetAccountId", request.targetAccountId(),
764
+ "amount", request.amount().toString(),
765
+ "description", request.description()
766
+ ))
767
+ .ipAddress(getClientIpAddress())
768
+ .timestamp(LocalDateTime.now())
769
+ .build();
770
+
771
+ // Store in database
772
+ auditRepository.save(event);
773
+
774
+ // Publish to Kafka for real-time monitoring
775
+ kafkaTemplate.send("audit-events", event);
776
+ }
777
+
778
+ public void logSuspiciousActivity(TransferRequest request, FraudCheckResult fraudCheck) {
779
+ AuditEvent event = AuditEvent.builder()
780
+ .eventType("FRAUD_ALERT")
781
+ .userId(SecurityContextHolder.getContext().getAuthentication().getName())
782
+ .action("TRANSFER_BLOCKED")
783
+ .status("SUSPICIOUS")
784
+ .details(Map.of(
785
+ "reason", fraudCheck.getReason(),
786
+ "riskScore", fraudCheck.getRiskScore(),
787
+ "amount", request.amount().toString()
788
+ ))
789
+ .timestamp(LocalDateTime.now())
790
+ .build();
791
+
792
+ auditRepository.save(event);
793
+ kafkaTemplate.send("fraud-alerts", event);
794
+ }
795
+ }
796
+ ```
797
+
798
+ ### 3. Data Encryption
799
+
800
+ **Encryption at Rest and in Transit**
801
+
802
+ ```java
803
+ // src/main/java/com/bank/infrastructure/security/EncryptionService.java
804
+ @Service
805
+ public class EncryptionService {
806
+ private final KeyManagementService kms;
807
+
808
+ public String encryptSensitiveData(String plaintext) {
809
+ try {
810
+ // Use AWS KMS or HSM for key management
811
+ byte[] dataKey = kms.generateDataKey();
812
+
813
+ Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
814
+ GCMParameterSpec spec = new GCMParameterSpec(128, generateIV());
815
+ SecretKeySpec keySpec = new SecretKeySpec(dataKey, "AES");
816
+
817
+ cipher.init(Cipher.ENCRYPT_MODE, keySpec, spec);
818
+ byte[] encrypted = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
819
+
820
+ return Base64.getEncoder().encodeToString(encrypted);
821
+ } catch (Exception e) {
822
+ throw new EncryptionException("Failed to encrypt data", e);
823
+ }
824
+ }
825
+
826
+ public String decryptSensitiveData(String ciphertext) {
827
+ // Decryption logic
828
+ }
829
+ }
830
+ ```
831
+
832
+ ---
833
+
834
+ ## Compliance & Regulatory
835
+
836
+ ### 1. SOX Compliance
837
+
838
+ **Segregation of Duties**
839
+
840
+ ```java
841
+ // src/main/java/com/bank/infrastructure/compliance/SoxComplianceService.java
842
+ @Service
843
+ public class SoxComplianceService {
844
+
845
+ @PreAuthorize("hasRole('MAKER')")
846
+ public void createTransaction(TransactionRequest request) {
847
+ // Maker creates transaction in PENDING state
848
+ Transaction transaction = Transaction.createPending(request);
849
+ transactionRepository.save(transaction);
850
+ }
851
+
852
+ @PreAuthorize("hasRole('CHECKER') and !hasRole('MAKER')")
853
+ public void approveTransaction(String transactionId) {
854
+ // Checker approves (different person than maker)
855
+ Transaction transaction = transactionRepository.findById(transactionId);
856
+
857
+ if (transaction.getCreatedBy().equals(getCurrentUser())) {
858
+ throw new SoxViolationException("Cannot approve own transaction");
859
+ }
860
+
861
+ transaction.approve(getCurrentUser());
862
+ transactionRepository.save(transaction);
863
+ }
864
+ }
865
+ ```
866
+
867
+ ### 2. GDPR Compliance
868
+
869
+ **Data Privacy and Right to be Forgotten**
870
+
871
+ ```java
872
+ // src/main/java/com/bank/application/gdpr/GdprService.java
873
+ @Service
874
+ public class GdprService {
875
+
876
+ public CustomerDataExport exportCustomerData(CustomerId customerId) {
877
+ // Export all customer data
878
+ Customer customer = customerRepository.findById(customerId);
879
+ List<Account> accounts = accountRepository.findByCustomerId(customerId);
880
+ List<Transaction> transactions = transactionRepository.findByCustomerId(customerId);
881
+
882
+ return CustomerDataExport.builder()
883
+ .customer(customer)
884
+ .accounts(accounts)
885
+ .transactions(transactions)
886
+ .exportDate(LocalDateTime.now())
887
+ .build();
888
+ }
889
+
890
+ @Transactional
891
+ public void anonymizeCustomerData(CustomerId customerId) {
892
+ // Anonymize customer data (cannot delete due to regulatory requirements)
893
+ Customer customer = customerRepository.findById(customerId);
894
+ customer.anonymize(); // Replace PII with anonymized values
895
+
896
+ customerRepository.save(customer);
897
+
898
+ auditLogger.log("GDPR_ANONYMIZATION", customerId);
899
+ }
900
+ }
901
+ ```
902
+
903
+ ---
904
+
905
+ ## Key Takeaways
906
+
907
+ ### Architecture Decisions
908
+
909
+ 1. **Layered Architecture**: Clear separation of concerns, easier to understand and maintain
910
+ 2. **Rich Domain Model**: Business logic in domain layer, not anemic models
911
+ 3. **Repository Pattern**: Abstracts data access, enables testing
912
+ 4. **ACID Transactions**: PostgreSQL ensures data integrity
913
+ 5. **Pessimistic Locking**: Prevents concurrent modification of accounts
914
+ 6. **Audit Logging**: Complete trail for compliance
915
+ 7. **Encryption**: AES-256 for sensitive data
916
+
917
+ ### Trade-offs
918
+
919
+ **Benefits**
920
+ - ✅ Clear separation of concerns
921
+ - ✅ Easy to understand and maintain
922
+ - ✅ Strong data consistency (ACID)
923
+ - ✅ Comprehensive security
924
+ - ✅ Regulatory compliance
925
+ - ✅ Testable layers
926
+
927
+ **Challenges**
928
+ - ❌ Monolithic deployment (all layers together)
929
+ - ❌ Vertical scaling only
930
+ - ❌ Technology lock-in (Java/Spring)
931
+ - ❌ Layer overhead for simple operations
932
+ - ❌ Tight coupling within monolith
933
+
934
+ ### Security Measures
935
+
936
+ - **Authentication**: OAuth 2.0, JWT tokens
937
+ - **Authorization**: Role-based access control (RBAC)
938
+ - **Encryption**: AES-256 at rest, TLS 1.3 in transit
939
+ - **MFA**: TOTP, SMS, biometric
940
+ - **Audit**: Complete audit trail
941
+ - **Compliance**: SOX, PCI DSS, GDPR, Basel III
942
+
943
+ ### Performance Metrics
944
+
945
+ - **Transaction Processing**: < 500ms (P95)
946
+ - **API Response Time**: < 200ms (P95)
947
+ - **Database Queries**: < 100ms (P95)
948
+ - **Availability**: 99.95%
949
+ - **Throughput**: 100,000 transactions/day
950
+
951
+ ---
952
+
953
+ ## References
954
+
955
+ - [Layered Architecture](../rules/layered-architecture.md)
956
+ - [Security Architecture](../rules/security.md)
957
+ - [Domain-Driven Design](../rules/tools-methodologies.md)
958
+ - [Quality Attributes](../rules/quality-attributes.md)
959
+ - [Spring Security Documentation](https://spring.io/projects/spring-security)
960
+ - [PCI DSS Compliance](https://www.pcisecuritystandards.org/)
961
+