@mytechtoday/augment-extensions 1.3.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/LICENSE +22 -22
  2. package/augment-extensions/domain-rules/software-architecture/README.md +143 -143
  3. package/augment-extensions/domain-rules/software-architecture/examples/banking-layered.md +961 -961
  4. package/augment-extensions/domain-rules/software-architecture/examples/ecommerce-microservices.md +990 -990
  5. package/augment-extensions/domain-rules/software-architecture/examples/iot-eventdriven.md +882 -882
  6. package/augment-extensions/domain-rules/software-architecture/examples/monolith-to-microservices-migration.md +703 -703
  7. package/augment-extensions/domain-rules/software-architecture/examples/serverless-imageprocessing.md +957 -957
  8. package/augment-extensions/domain-rules/software-architecture/examples/trading-eventdriven.md +747 -747
  9. package/augment-extensions/domain-rules/software-architecture/module.json +119 -119
  10. package/augment-extensions/domain-rules/software-architecture/rules/challenges-solutions.md +763 -763
  11. package/augment-extensions/domain-rules/software-architecture/rules/definitions-terminology.md +409 -409
  12. package/augment-extensions/domain-rules/software-architecture/rules/design-principles.md +684 -684
  13. package/augment-extensions/domain-rules/software-architecture/rules/evaluation-testing.md +1381 -1381
  14. package/augment-extensions/domain-rules/software-architecture/rules/event-driven-architecture.md +616 -616
  15. package/augment-extensions/domain-rules/software-architecture/rules/fundamentals.md +306 -306
  16. package/augment-extensions/domain-rules/software-architecture/rules/industry-architectures.md +554 -554
  17. package/augment-extensions/domain-rules/software-architecture/rules/layered-architecture.md +776 -776
  18. package/augment-extensions/domain-rules/software-architecture/rules/microservices-architecture.md +503 -503
  19. package/augment-extensions/domain-rules/software-architecture/rules/modeling-documentation.md +1199 -1199
  20. package/augment-extensions/domain-rules/software-architecture/rules/monolithic-architecture.md +351 -351
  21. package/augment-extensions/domain-rules/software-architecture/rules/principles.md +556 -556
  22. package/augment-extensions/domain-rules/software-architecture/rules/quality-attributes.md +797 -797
  23. package/augment-extensions/domain-rules/software-architecture/rules/scalability-performance.md +1345 -1345
  24. package/augment-extensions/domain-rules/software-architecture/rules/security-architecture.md +1039 -1039
  25. package/augment-extensions/domain-rules/software-architecture/rules/serverless-architecture.md +711 -711
  26. package/augment-extensions/domain-rules/software-architecture/rules/skills-development.md +568 -568
  27. package/augment-extensions/domain-rules/software-architecture/rules/tools-methodologies.md +961 -961
  28. package/augment-extensions/visual-design/CHANGELOG.md +132 -132
  29. package/augment-extensions/visual-design/README.md +255 -255
  30. package/augment-extensions/visual-design/__tests__/README.md +119 -119
  31. package/augment-extensions/visual-design/__tests__/style-selector.test.ts +172 -172
  32. package/augment-extensions/visual-design/__tests__/vendor-styles.test.ts +214 -214
  33. package/augment-extensions/visual-design/domains/other/ai-prompt-helper.ts +157 -157
  34. package/augment-extensions/visual-design/domains/other/dotnet-application.ts +156 -156
  35. package/augment-extensions/visual-design/domains/other/linux-platform.ts +156 -156
  36. package/augment-extensions/visual-design/domains/other/mobile-application.ts +157 -157
  37. package/augment-extensions/visual-design/domains/other/motion-picture.ts +156 -156
  38. package/augment-extensions/visual-design/domains/other/os-application.ts +156 -156
  39. package/augment-extensions/visual-design/domains/other/print-campaigns.ts +158 -158
  40. package/augment-extensions/visual-design/domains/other/web-app.ts +157 -157
  41. package/augment-extensions/visual-design/domains/other/website.ts +161 -161
  42. package/augment-extensions/visual-design/domains/other/windows-platform.ts +156 -156
  43. package/augment-extensions/visual-design/domains/web-page-styles/amazon-cloudscape.ts +506 -506
  44. package/augment-extensions/visual-design/domains/web-page-styles/google-modern.ts +615 -615
  45. package/augment-extensions/visual-design/domains/web-page-styles/microsoft-fluent.ts +531 -531
  46. package/augment-extensions/visual-design/examples/README.md +97 -97
  47. package/augment-extensions/visual-design/examples/ai-prompt-generation.md +233 -233
  48. package/augment-extensions/visual-design/examples/basic-usage.md +216 -216
  49. package/augment-extensions/visual-design/examples/domain-workflows.md +257 -257
  50. package/augment-extensions/visual-design/examples/vendor-comparison.md +247 -247
  51. package/augment-extensions/visual-design/module.json +78 -78
  52. package/augment-extensions/visual-design/style-selector.ts +177 -177
  53. package/augment-extensions/visual-design/types.ts +302 -302
  54. package/augment-extensions/visual-design/visual-design-core.ts +469 -469
  55. package/augment-extensions/workflows/adr-support/README.md +227 -227
  56. package/augment-extensions/workflows/adr-support/__tests__/adr-validator.test.ts +203 -203
  57. package/augment-extensions/workflows/adr-support/adr-validator.ts +162 -162
  58. package/augment-extensions/workflows/adr-support/examples/complete-lifecycle-example.md +449 -449
  59. package/augment-extensions/workflows/adr-support/examples/integration-example.md +580 -580
  60. package/augment-extensions/workflows/adr-support/examples/superseding-example.md +436 -436
  61. package/augment-extensions/workflows/adr-support/module.json +112 -112
  62. package/augment-extensions/workflows/adr-support/rules/adr-creation.md +372 -372
  63. package/augment-extensions/workflows/adr-support/rules/beads-integration.md +443 -443
  64. package/augment-extensions/workflows/adr-support/rules/conflict-detection.md +486 -486
  65. package/augment-extensions/workflows/adr-support/rules/decision-detection.md +362 -362
  66. package/augment-extensions/workflows/adr-support/rules/lifecycle-management.md +427 -427
  67. package/augment-extensions/workflows/adr-support/rules/openspec-integration.md +465 -465
  68. package/augment-extensions/workflows/adr-support/rules/template-selection.md +405 -405
  69. package/augment-extensions/workflows/adr-support/rules/validation-rules.md +543 -543
  70. package/augment-extensions/workflows/adr-support/schemas/adr-config.json +191 -191
  71. package/augment-extensions/workflows/adr-support/schemas/adr-metadata.json +172 -172
  72. package/augment-extensions/workflows/adr-support/templates/business-case.md +235 -235
  73. package/augment-extensions/workflows/adr-support/templates/madr-elaborate.md +197 -197
  74. package/augment-extensions/workflows/adr-support/templates/madr-simple.md +68 -68
  75. package/augment-extensions/workflows/adr-support/templates/nygard.md +84 -84
  76. package/augment-extensions/writing-standards/screenplay/rules/file-organization.md +213 -213
  77. package/augment-extensions/writing-standards/screenplay/utils/__tests__/file-organization.test.ts +169 -169
  78. package/augment-extensions/writing-standards/screenplay/utils/file-organization.ts +165 -165
  79. package/cli/dist/utils/auto-sync.js +19 -19
  80. package/package.json +5 -3
  81. package/augment-extensions/workflows/openspec/README.md +0 -96
  82. package/augment-extensions/workflows/openspec/examples/complete-change-example.md +0 -244
  83. package/augment-extensions/workflows/openspec/module.json +0 -54
  84. package/augment-extensions/workflows/openspec/rules/best-practices.md +0 -272
  85. package/augment-extensions/workflows/openspec/rules/manual-setup.md +0 -231
  86. package/augment-extensions/workflows/openspec/rules/spec-format.md +0 -236
  87. package/augment-extensions/workflows/openspec/rules/workflow.md +0 -214
  88. package/cli/dist/utils/__tests__/adr-validator.example.d.ts +0 -6
  89. package/cli/dist/utils/__tests__/adr-validator.example.d.ts.map +0 -1
  90. package/cli/dist/utils/__tests__/adr-validator.example.js +0 -148
  91. package/cli/dist/utils/__tests__/adr-validator.example.js.map +0 -1
  92. package/cli/dist/utils/adr-validator.d.ts +0 -65
  93. package/cli/dist/utils/adr-validator.d.ts.map +0 -1
  94. package/cli/dist/utils/adr-validator.js +0 -203
  95. 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
+