Repository files navigation

BankingKata - Architecture Hexagonale

Application bancaire en architecture hexagonale (ports & adapters) avec .NET 8 et tests complets.

Architecture Hexagonale

graph TB
subgraph "Couche API (Drivers)"
API["API REST<br/>BankingKata.Api"]
end
subgraph "Couche Application (Use Cases)"
AS["BankAccountService"]
SAS["SavingsAccountService"]
end
subgraph "Couche Domaine (Core)"
BA["BankAccount"]
SA["SavingsAccount"]
T["Transaction"]
end
subgraph "Couche Infrastructure (Adapters)"
InMemRepo["InMemoryBankAccountRepository"]
InMemSavRepo["InMemorySavingsAccountRepository"]
InMemTxRepo["InMemoryTransactionRepository"]
end
subgraph "Ports (Interfaces) - Domain"
IRepo["IBankAccountRepository"]
ISavRepo["ISavingsAccountRepository"]
ITxRepo["ITransactionRepository"]
end
API --> AS
API --> SAS
AS --> BA
AS --> IRepo
AS --> ITxRepo
AS --> T
SAS --> SA
SAS --> ISavRepo
SAS --> ITxRepo
SAS --> T
IRepo -.->|implémenté par| InMemRepo
ISavRepo -.->|implémenté par| InMemSavRepo
ITxRepo -.->|implémenté par| InMemTxRepo
Loading

Flux de Données

sequenceDiagram
participant Client
participant Controller
participant Service
participant Repository
participant DB
Note over Client,DB: COMMAND (Écriture)
Client->>Controller: POST /api/accounts
Controller->>Service: CreateAccount(dto)
Service->>Domain: new BankAccount()
Domain-->>Service: account
Service->>Repository: Save(account)
Repository->>DB: INSERT
DB-->>Repository: success
Repository-->>Service: saved
Service-->>Controller: result
Controller-->>Client: 201 Created
Note over Client,DB: QUERY (Lecture)
Client->>Controller: GET /api/accounts/1
Controller->>Service: GetAccount(1)
Service->>Repository: FindById(1)
Repository->>DB: SELECT
DB-->>Repository: entity
Repository-->>Service: dto
Service-->>Controller: account
Controller-->>Client: 200 OK
Loading

Structure du Projet

BankingKata/
├── BankingKata.sln
│
├── BankingKata.Domain/ # 🟢 Core - Règles métier pures
│ ├── Entities/
│ │ ├── BankAccount.cs # Compte courant
│ │ ├── SavingsAccount.cs # Livret d'épargne
│ │ └── Transaction.cs # Opération
│ └── Ports/ # Interfaces (contrats métier)
│ ├── IBankAccountRepository.cs
│ ├── ISavingsAccountRepository.cs
│ └── ITransactionRepository.cs
│
├── BankingKata.Application/ # 🟡 Use Cases
│ ├── DTOs/ # Data Transfer Objects
│ │ ├── BankAccountDto.cs
│ │ ├── SavingsAccountDto.cs
│ │ └── StatementDto.cs
│ └── UseCases/ # Logique applicative
│ ├── AccountServices.cs # BankAccountService + SavingsAccountService unifiés
│
├── BankingKata.Infrastructure/ # 🔵 Adapters - Implémentations
│ └── Persistence/
│ ├── InMemoryBankAccountRepository.cs
│ ├── InMemorySavingsAccountRepository.cs
│ └── InMemoryTransactionRepository.cs
│
├── BankingKata.Api/ # 🚀 API REST
│ ├── Controllers/
│ │ ├── AccountsController.cs
│ │ └── SavingsController.cs
│ ├── Program.cs
│ └── Properties/
│
├── BankingKata.Tests/ # 🧪 Tests Unitaires
│ ├── BankAccountTests.cs
│ ├── BankAccountServiceTests.cs
│ ├── SavingsAccountTests.cs
│ └── SavingsAccountServiceTests.cs
│
├── BankingKata.Api.Tests/ # 🧪 Tests d'Intégration
│ ├── AccountsControllerTests.cs
│ └── SavingsControllerTests.cs
│
└── .github/workflows/
└── ci.yml # Pipeline CI

Principes de l'Architecture Hexagonale

PrincipeImplémentation
Indépendance du domaineBankingKata.Domain n'a aucune dépendance externe
Dependency RuleLe Domain définit les ports; les couches extérieures dépendent du Domain
Ports (interfaces)Dans Domain/Ports/ : IBankAccountRepository, ITransactionRepository
AdaptersImplémentations concrètes dans Infrastructure/Persistence/
Use CasesBankAccountService, SavingsAccountService dans Application/UseCases/
Injection de dépendances.NET DI container dans Program.cs

Fonctionnalités

Feature 1 : Compte Bancaire

Compte courant avec dépôt et retrait.

FonctionnalitéDescription
Numéro de compteIdentifiant unique
SoldeMontant actuel
DépôtAjout d'argent
RetraitRetrait avec vérification du solde

Règle métier : Un retrait ne peut pas dépasser le solde disponible.

Feature 2 : Découvert Autorisé

Extension du compte courant avec une autorisation de découvert.

FonctionnalitéDescription
OverdraftLimitMontant maximum du découvert
Retrait étenduAutorisé jusqu'à solde + découvert

Règle métier : Un retrait est autorisé si montant ≤ solde + autorisation_decouvert.

Feature 3 : Livret d'Épargne

Compte avec plafond de dépôt, sans découvert possible.

FonctionnalitéDescription
DepositCeilingPlafond maximum de dépôt
Dépôt limitéVérification du plafond
Pas de découvertRetrait limité au solde

Règle métier : Un dépôt ne peut pas dépasser le plafond du livret.

Feature 4 : Relevé de Compte

Historique des opérations sur un mois glissant.

FonctionnalitéDescription
Type de compte"Compte Courant" ou "Livret"
Solde actuelBalance à la date d'émission
OpérationsListe triée antéchronologique

API Endpoints

Comptes Courants

MéthodeEndpointDescriptionCorps
GET/api/accountsListe tous les comptes-
GET/api/accounts/{accountNumber}Récupère un compte-
POST/api/accountsCrée un compteCreateAccountDto
POST/api/accounts/{accountNumber}/depositDépôtTransactionDto
POST/api/accounts/{accountNumber}/withdrawRetraitTransactionDto
PATCH/api/accounts/{accountNumber}/overdraftModifie le découvertSetOverdraftDto
GET/api/accounts/{accountNumber}/statementRelevé de compteQuery params: fromDate, toDate

Livrets d'Épargne

MéthodeEndpointDescriptionCorps
GET/api/savingsListe tous les livrets-
GET/api/savings/{accountNumber}Récupère un livret-
POST/api/savingsCrée un livretCreateSavingsAccountDto
POST/api/savings/{accountNumber}/depositDépôtSavingsTransactionDto
POST/api/savings/{accountNumber}/withdrawRetraitSavingsTransactionDto
GET/api/savings/{accountNumber}/statementRelevé de livretQuery params: fromDate, toDate

DTOs (Data Transfer Objects)

BankAccountDto

{
"accountNumber": "ACC001",
"balance": 1000.00,
"overdraftLimit": 500.00
}

CreateAccountDto

{
"accountNumber": "ACC001",
"initialBalance": 1000.00,
"overdraftLimit": 500.00
}

SavingsAccountDto

{
"accountNumber": "SAV001",
"balance": 5000.00,
"depositCeiling": 22950.00
}

CreateSavingsAccountDto

{
"accountNumber": "SAV001",
"depositCeiling": 22950.00,
"initialBalance": 1000.00
}

StatementDto (Relevé)

{
"accountNumber": "ACC001",
"accountType": "Compte Courant",
"currentBalance": 1200.00,
"statementDate": "2026-04-12T12:00:00Z",
"operations": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"accountNumber": "ACC001",
"amount": 500.00,
"type": "Deposit",
"date": "2026-04-12T11:30:00Z",
"balanceAfterTransaction": 1500.00
}
]
}

Installation et Lancement

Prérequis

  • .NET 8.0 SDK
  • (Optionnel) Node.js pour le frontend React

Lancer l'API

cd BankingKata/BankingKata.Api
dotnet run

L'API sera disponible sur http://0.0.0.0:5000

Swagger UI accessible sur http://0.0.0.0:5000/swagger

Lancer les Tests

dotnet test

Structure des Tests

ProjetTypeCouverture
BankingKata.TestsUnitairesDomain + Application
BankingKata.Api.TestsIntégrationAPI REST

Exemples d'Utilisation

Créer un compte courant avec découvert

curl -X POST http://localhost:5000/api/accounts \
-H "Content-Type: application/json" \
-d '{"accountNumber": "ACC001", "initialBalance": 1000, "overdraftLimit": 500}'

Effectuer un dépôt

curl -X POST http://localhost:5000/api/accounts/ACC001/deposit \
-H "Content-Type: application/json" \
-d '{"amount": 250}'

Effectuer un retrait (avec découvert)

curl -X POST http://localhost:5000/api/accounts/ACC001/withdraw \
-H "Content-Type: application/json" \
-d '{"amount": 1200}'

Créer un livret d'épargne

curl -X POST http://localhost:5000/api/savings \
-H "Content-Type: application/json" \
-d '{"accountNumber": "SAV001", "depositCeiling": 22950, "initialBalance": 5000}'

Obtenir un relevé

curl "http://localhost:5000/api/accounts/ACC001/statement"

Obtenir un relevé sur une période

curl "http://localhost:5000/api/accounts/ACC001/statement?fromDate=2026-03-01&toDate=2026-04-12"

Décision de Design : TransactionRepository Shared

Une décision de design importante : les deux types de comptes (BankAccount et SavingsAccount) partagent le même ITransactionRepository.

Rationalité :

  • Un client peut avoir plusieurs comptes (courant + livret)
  • Un relevé consolidé pourrait être nécessaire
  • Simplifie la persistence (une seule table/collection)

Alternative possible : Un TransactionRepository par type de compte si isolation stricte requise.

Statuts HTTP

CodeSignification
200 OKSuccès
201 CreatedRessource créée
400 Bad RequestErreur de validation
404 Not FoundRessource non trouvée
409 ConflictRessource déjà existante

CI Pipeline

GitHub Actions (.github/workflows/ci.yml)

ÉtapeDescription
CheckoutRécupération du code
Setup .NETInstallation .NET 8
RestoreRestauration des dépendances
BuildCompilation en Release
TestsTests unitaires + d'intégration

About

[C#] POC of a Banking API basic system (Hexagonal architecture)

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

BankingKata - Architecture Hexagonale

Application bancaire en architecture hexagonale (ports & adapters) avec .NET 8 et tests complets.

Architecture Hexagonale

graph TB
subgraph "Couche API (Drivers)"
API["API REST<br/>BankingKata.Api"]
end
subgraph "Couche Application (Use Cases)"
AS["BankAccountService"]
SAS["SavingsAccountService"]
end
subgraph "Couche Domaine (Core)"
BA["BankAccount"]
SA["SavingsAccount"]
T["Transaction"]
end
subgraph "Couche Infrastructure (Adapters)"
InMemRepo["InMemoryBankAccountRepository"]
InMemSavRepo["InMemorySavingsAccountRepository"]
InMemTxRepo["InMemoryTransactionRepository"]
end
subgraph "Ports (Interfaces) - Domain"
IRepo["IBankAccountRepository"]
ISavRepo["ISavingsAccountRepository"]
ITxRepo["ITransactionRepository"]
end
API --> AS
API --> SAS
AS --> BA
AS --> IRepo
AS --> ITxRepo
AS --> T
SAS --> SA
SAS --> ISavRepo
SAS --> ITxRepo
SAS --> T
IRepo -.->|implémenté par| InMemRepo
ISavRepo -.->|implémenté par| InMemSavRepo
ITxRepo -.->|implémenté par| InMemTxRepo
Loading

Flux de Données

sequenceDiagram
participant Client
participant Controller
participant Service
participant Repository
participant DB
Note over Client,DB: COMMAND (Écriture)
Client->>Controller: POST /api/accounts
Controller->>Service: CreateAccount(dto)
Service->>Domain: new BankAccount()
Domain-->>Service: account
Service->>Repository: Save(account)
Repository->>DB: INSERT
DB-->>Repository: success
Repository-->>Service: saved
Service-->>Controller: result
Controller-->>Client: 201 Created
Note over Client,DB: QUERY (Lecture)
Client->>Controller: GET /api/accounts/1
Controller->>Service: GetAccount(1)
Service->>Repository: FindById(1)
Repository->>DB: SELECT
DB-->>Repository: entity
Repository-->>Service: dto
Service-->>Controller: account
Controller-->>Client: 200 OK
Loading

Structure du Projet

BankingKata/
├── BankingKata.sln
│
├── BankingKata.Domain/ # 🟢 Core - Règles métier pures
│ ├── Entities/
│ │ ├── BankAccount.cs # Compte courant
│ │ ├── SavingsAccount.cs # Livret d'épargne
│ │ └── Transaction.cs # Opération
│ └── Ports/ # Interfaces (contrats métier)
│ ├── IBankAccountRepository.cs
│ ├── ISavingsAccountRepository.cs
│ └── ITransactionRepository.cs
│
├── BankingKata.Application/ # 🟡 Use Cases
│ ├── DTOs/ # Data Transfer Objects
│ │ ├── BankAccountDto.cs
│ │ ├── SavingsAccountDto.cs
│ │ └── StatementDto.cs
│ └── UseCases/ # Logique applicative
│ ├── AccountServices.cs # BankAccountService + SavingsAccountService unifiés
│
├── BankingKata.Infrastructure/ # 🔵 Adapters - Implémentations
│ └── Persistence/
│ ├── InMemoryBankAccountRepository.cs
│ ├── InMemorySavingsAccountRepository.cs
│ └── InMemoryTransactionRepository.cs
│
├── BankingKata.Api/ # 🚀 API REST
│ ├── Controllers/
│ │ ├── AccountsController.cs
│ │ └── SavingsController.cs
│ ├── Program.cs
│ └── Properties/
│
├── BankingKata.Tests/ # 🧪 Tests Unitaires
│ ├── BankAccountTests.cs
│ ├── BankAccountServiceTests.cs
│ ├── SavingsAccountTests.cs
│ └── SavingsAccountServiceTests.cs
│
├── BankingKata.Api.Tests/ # 🧪 Tests d'Intégration
│ ├── AccountsControllerTests.cs
│ └── SavingsControllerTests.cs
│
└── .github/workflows/
└── ci.yml # Pipeline CI

Principes de l'Architecture Hexagonale

PrincipeImplémentation
Indépendance du domaineBankingKata.Domain n'a aucune dépendance externe
Dependency RuleLe Domain définit les ports; les couches extérieures dépendent du Domain
Ports (interfaces)Dans Domain/Ports/ : IBankAccountRepository, ITransactionRepository
AdaptersImplémentations concrètes dans Infrastructure/Persistence/
Use CasesBankAccountService, SavingsAccountService dans Application/UseCases/
Injection de dépendances.NET DI container dans Program.cs

Fonctionnalités

Feature 1 : Compte Bancaire

Compte courant avec dépôt et retrait.

FonctionnalitéDescription
Numéro de compteIdentifiant unique
SoldeMontant actuel
DépôtAjout d'argent
RetraitRetrait avec vérification du solde

Règle métier : Un retrait ne peut pas dépasser le solde disponible.

Feature 2 : Découvert Autorisé

Extension du compte courant avec une autorisation de découvert.

FonctionnalitéDescription
OverdraftLimitMontant maximum du découvert
Retrait étenduAutorisé jusqu'à solde + découvert

Règle métier : Un retrait est autorisé si montant ≤ solde + autorisation_decouvert.

Feature 3 : Livret d'Épargne

Compte avec plafond de dépôt, sans découvert possible.

FonctionnalitéDescription
DepositCeilingPlafond maximum de dépôt
Dépôt limitéVérification du plafond
Pas de découvertRetrait limité au solde

Règle métier : Un dépôt ne peut pas dépasser le plafond du livret.

Feature 4 : Relevé de Compte

Historique des opérations sur un mois glissant.

FonctionnalitéDescription
Type de compte"Compte Courant" ou "Livret"
Solde actuelBalance à la date d'émission
OpérationsListe triée antéchronologique

API Endpoints

Comptes Courants

MéthodeEndpointDescriptionCorps
GET/api/accountsListe tous les comptes-
GET/api/accounts/{accountNumber}Récupère un compte-
POST/api/accountsCrée un compteCreateAccountDto
POST/api/accounts/{accountNumber}/depositDépôtTransactionDto
POST/api/accounts/{accountNumber}/withdrawRetraitTransactionDto
PATCH/api/accounts/{accountNumber}/overdraftModifie le découvertSetOverdraftDto
GET/api/accounts/{accountNumber}/statementRelevé de compteQuery params: fromDate, toDate

Livrets d'Épargne

MéthodeEndpointDescriptionCorps
GET/api/savingsListe tous les livrets-
GET/api/savings/{accountNumber}Récupère un livret-
POST/api/savingsCrée un livretCreateSavingsAccountDto
POST/api/savings/{accountNumber}/depositDépôtSavingsTransactionDto
POST/api/savings/{accountNumber}/withdrawRetraitSavingsTransactionDto
GET/api/savings/{accountNumber}/statementRelevé de livretQuery params: fromDate, toDate

DTOs (Data Transfer Objects)

BankAccountDto

{
"accountNumber": "ACC001",
"balance": 1000.00,
"overdraftLimit": 500.00
}

CreateAccountDto

{
"accountNumber": "ACC001",
"initialBalance": 1000.00,
"overdraftLimit": 500.00
}

SavingsAccountDto

{
"accountNumber": "SAV001",
"balance": 5000.00,
"depositCeiling": 22950.00
}

CreateSavingsAccountDto

{
"accountNumber": "SAV001",
"depositCeiling": 22950.00,
"initialBalance": 1000.00
}

StatementDto (Relevé)

{
"accountNumber": "ACC001",
"accountType": "Compte Courant",
"currentBalance": 1200.00,
"statementDate": "2026-04-12T12:00:00Z",
"operations": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"accountNumber": "ACC001",
"amount": 500.00,
"type": "Deposit",
"date": "2026-04-12T11:30:00Z",
"balanceAfterTransaction": 1500.00
}
]
}

Installation et Lancement

Prérequis

  • .NET 8.0 SDK
  • (Optionnel) Node.js pour le frontend React

Lancer l'API

cd BankingKata/BankingKata.Api
dotnet run

L'API sera disponible sur http://0.0.0.0:5000

Swagger UI accessible sur http://0.0.0.0:5000/swagger

Lancer les Tests

dotnet test

Structure des Tests

ProjetTypeCouverture
BankingKata.TestsUnitairesDomain + Application
BankingKata.Api.TestsIntégrationAPI REST

Exemples d'Utilisation

Créer un compte courant avec découvert

curl -X POST http://localhost:5000/api/accounts \
-H "Content-Type: application/json" \
-d '{"accountNumber": "ACC001", "initialBalance": 1000, "overdraftLimit": 500}'

Effectuer un dépôt

curl -X POST http://localhost:5000/api/accounts/ACC001/deposit \
-H "Content-Type: application/json" \
-d '{"amount": 250}'

Effectuer un retrait (avec découvert)

curl -X POST http://localhost:5000/api/accounts/ACC001/withdraw \
-H "Content-Type: application/json" \
-d '{"amount": 1200}'

Créer un livret d'épargne

curl -X POST http://localhost:5000/api/savings \
-H "Content-Type: application/json" \
-d '{"accountNumber": "SAV001", "depositCeiling": 22950, "initialBalance": 5000}'

Obtenir un relevé

curl "http://localhost:5000/api/accounts/ACC001/statement"

Obtenir un relevé sur une période

curl "http://localhost:5000/api/accounts/ACC001/statement?fromDate=2026-03-01&toDate=2026-04-12"

Décision de Design : TransactionRepository Shared

Une décision de design importante : les deux types de comptes (BankAccount et SavingsAccount) partagent le même ITransactionRepository.

Rationalité :

  • Un client peut avoir plusieurs comptes (courant + livret)
  • Un relevé consolidé pourrait être nécessaire
  • Simplifie la persistence (une seule table/collection)

Alternative possible : Un TransactionRepository par type de compte si isolation stricte requise.

Statuts HTTP

CodeSignification
200 OKSuccès
201 CreatedRessource créée
400 Bad RequestErreur de validation
404 Not FoundRessource non trouvée
409 ConflictRessource déjà existante

CI Pipeline

GitHub Actions (.github/workflows/ci.yml)

ÉtapeDescription
CheckoutRécupération du code
Setup .NETInstallation .NET 8
RestoreRestauration des dépendances
BuildCompilation en Release
TestsTests unitaires + d'intégration

About

[C#] POC of a Banking API basic system (Hexagonal architecture)

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

BankingKata - Architecture Hexagonale

Application bancaire en architecture hexagonale (ports & adapters) avec .NET 8 et tests complets.

Architecture Hexagonale

graph TB
subgraph "Couche API (Drivers)"
API["API REST<br/>BankingKata.Api"]
end
subgraph "Couche Application (Use Cases)"
AS["BankAccountService"]
SAS["SavingsAccountService"]
end
subgraph "Couche Domaine (Core)"
BA["BankAccount"]
SA["SavingsAccount"]
T["Transaction"]
end
subgraph "Couche Infrastructure (Adapters)"
InMemRepo["InMemoryBankAccountRepository"]
InMemSavRepo["InMemorySavingsAccountRepository"]
InMemTxRepo["InMemoryTransactionRepository"]
end
subgraph "Ports (Interfaces) - Domain"
IRepo["IBankAccountRepository"]
ISavRepo["ISavingsAccountRepository"]
ITxRepo["ITransactionRepository"]
end
API --> AS
API --> SAS
AS --> BA
AS --> IRepo
AS --> ITxRepo
AS --> T
SAS --> SA
SAS --> ISavRepo
SAS --> ITxRepo
SAS --> T
IRepo -.->|implémenté par| InMemRepo
ISavRepo -.->|implémenté par| InMemSavRepo
ITxRepo -.->|implémenté par| InMemTxRepo
Loading

Flux de Données

sequenceDiagram
participant Client
participant Controller
participant Service
participant Repository
participant DB
Note over Client,DB: COMMAND (Écriture)
Client->>Controller: POST /api/accounts
Controller->>Service: CreateAccount(dto)
Service->>Domain: new BankAccount()
Domain-->>Service: account
Service->>Repository: Save(account)
Repository->>DB: INSERT
DB-->>Repository: success
Repository-->>Service: saved
Service-->>Controller: result
Controller-->>Client: 201 Created
Note over Client,DB: QUERY (Lecture)
Client->>Controller: GET /api/accounts/1
Controller->>Service: GetAccount(1)
Service->>Repository: FindById(1)
Repository->>DB: SELECT
DB-->>Repository: entity
Repository-->>Service: dto
Service-->>Controller: account
Controller-->>Client: 200 OK
Loading

Structure du Projet

BankingKata/
├── BankingKata.sln
│
├── BankingKata.Domain/ # 🟢 Core - Règles métier pures
│ ├── Entities/
│ │ ├── BankAccount.cs # Compte courant
│ │ ├── SavingsAccount.cs # Livret d'épargne
│ │ └── Transaction.cs # Opération
│ └── Ports/ # Interfaces (contrats métier)
│ ├── IBankAccountRepository.cs
│ ├── ISavingsAccountRepository.cs
│ └── ITransactionRepository.cs
│
├── BankingKata.Application/ # 🟡 Use Cases
│ ├── DTOs/ # Data Transfer Objects
│ │ ├── BankAccountDto.cs
│ │ ├── SavingsAccountDto.cs
│ │ └── StatementDto.cs
│ └── UseCases/ # Logique applicative
│ ├── AccountServices.cs # BankAccountService + SavingsAccountService unifiés
│
├── BankingKata.Infrastructure/ # 🔵 Adapters - Implémentations
│ └── Persistence/
│ ├── InMemoryBankAccountRepository.cs
│ ├── InMemorySavingsAccountRepository.cs
│ └── InMemoryTransactionRepository.cs
│
├── BankingKata.Api/ # 🚀 API REST
│ ├── Controllers/
│ │ ├── AccountsController.cs
│ │ └── SavingsController.cs
│ ├── Program.cs
│ └── Properties/
│
├── BankingKata.Tests/ # 🧪 Tests Unitaires
│ ├── BankAccountTests.cs
│ ├── BankAccountServiceTests.cs
│ ├── SavingsAccountTests.cs
│ └── SavingsAccountServiceTests.cs
│
├── BankingKata.Api.Tests/ # 🧪 Tests d'Intégration
│ ├── AccountsControllerTests.cs
│ └── SavingsControllerTests.cs
│
└── .github/workflows/
└── ci.yml # Pipeline CI

Principes de l'Architecture Hexagonale

PrincipeImplémentation
Indépendance du domaineBankingKata.Domain n'a aucune dépendance externe
Dependency RuleLe Domain définit les ports; les couches extérieures dépendent du Domain
Ports (interfaces)Dans Domain/Ports/ : IBankAccountRepository, ITransactionRepository
AdaptersImplémentations concrètes dans Infrastructure/Persistence/
Use CasesBankAccountService, SavingsAccountService dans Application/UseCases/
Injection de dépendances.NET DI container dans Program.cs

Fonctionnalités

Feature 1 : Compte Bancaire

Compte courant avec dépôt et retrait.

FonctionnalitéDescription
Numéro de compteIdentifiant unique
SoldeMontant actuel
DépôtAjout d'argent
RetraitRetrait avec vérification du solde

Règle métier : Un retrait ne peut pas dépasser le solde disponible.

Feature 2 : Découvert Autorisé

Extension du compte courant avec une autorisation de découvert.

FonctionnalitéDescription
OverdraftLimitMontant maximum du découvert
Retrait étenduAutorisé jusqu'à solde + découvert

Règle métier : Un retrait est autorisé si montant ≤ solde + autorisation_decouvert.

Feature 3 : Livret d'Épargne

Compte avec plafond de dépôt, sans découvert possible.

FonctionnalitéDescription
DepositCeilingPlafond maximum de dépôt
Dépôt limitéVérification du plafond
Pas de découvertRetrait limité au solde

Règle métier : Un dépôt ne peut pas dépasser le plafond du livret.

Feature 4 : Relevé de Compte

Historique des opérations sur un mois glissant.

FonctionnalitéDescription
Type de compte"Compte Courant" ou "Livret"
Solde actuelBalance à la date d'émission
OpérationsListe triée antéchronologique

API Endpoints

Comptes Courants

MéthodeEndpointDescriptionCorps
GET/api/accountsListe tous les comptes-
GET/api/accounts/{accountNumber}Récupère un compte-
POST/api/accountsCrée un compteCreateAccountDto
POST/api/accounts/{accountNumber}/depositDépôtTransactionDto
POST/api/accounts/{accountNumber}/withdrawRetraitTransactionDto
PATCH/api/accounts/{accountNumber}/overdraftModifie le découvertSetOverdraftDto
GET/api/accounts/{accountNumber}/statementRelevé de compteQuery params: fromDate, toDate

Livrets d'Épargne

MéthodeEndpointDescriptionCorps
GET/api/savingsListe tous les livrets-
GET/api/savings/{accountNumber}Récupère un livret-
POST/api/savingsCrée un livretCreateSavingsAccountDto
POST/api/savings/{accountNumber}/depositDépôtSavingsTransactionDto
POST/api/savings/{accountNumber}/withdrawRetraitSavingsTransactionDto
GET/api/savings/{accountNumber}/statementRelevé de livretQuery params: fromDate, toDate

DTOs (Data Transfer Objects)

BankAccountDto

{
"accountNumber": "ACC001",
"balance": 1000.00,
"overdraftLimit": 500.00
}

CreateAccountDto

{
"accountNumber": "ACC001",
"initialBalance": 1000.00,
"overdraftLimit": 500.00
}

SavingsAccountDto

{
"accountNumber": "SAV001",
"balance": 5000.00,
"depositCeiling": 22950.00
}

CreateSavingsAccountDto

{
"accountNumber": "SAV001",
"depositCeiling": 22950.00,
"initialBalance": 1000.00
}

StatementDto (Relevé)

{
"accountNumber": "ACC001",
"accountType": "Compte Courant",
"currentBalance": 1200.00,
"statementDate": "2026-04-12T12:00:00Z",
"operations": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"accountNumber": "ACC001",
"amount": 500.00,
"type": "Deposit",
"date": "2026-04-12T11:30:00Z",
"balanceAfterTransaction": 1500.00
}
]
}

Installation et Lancement

Prérequis

  • .NET 8.0 SDK
  • (Optionnel) Node.js pour le frontend React

Lancer l'API

cd BankingKata/BankingKata.Api
dotnet run

L'API sera disponible sur http://0.0.0.0:5000

Swagger UI accessible sur http://0.0.0.0:5000/swagger

Lancer les Tests

dotnet test

Structure des Tests

ProjetTypeCouverture
BankingKata.TestsUnitairesDomain + Application
BankingKata.Api.TestsIntégrationAPI REST

Exemples d'Utilisation

Créer un compte courant avec découvert

curl -X POST http://localhost:5000/api/accounts \
-H "Content-Type: application/json" \
-d '{"accountNumber": "ACC001", "initialBalance": 1000, "overdraftLimit": 500}'

Effectuer un dépôt

curl -X POST http://localhost:5000/api/accounts/ACC001/deposit \
-H "Content-Type: application/json" \
-d '{"amount": 250}'

Effectuer un retrait (avec découvert)

curl -X POST http://localhost:5000/api/accounts/ACC001/withdraw \
-H "Content-Type: application/json" \
-d '{"amount": 1200}'

Créer un livret d'épargne

curl -X POST http://localhost:5000/api/savings \
-H "Content-Type: application/json" \
-d '{"accountNumber": "SAV001", "depositCeiling": 22950, "initialBalance": 5000}'

Obtenir un relevé

curl "http://localhost:5000/api/accounts/ACC001/statement"

Obtenir un relevé sur une période

curl "http://localhost:5000/api/accounts/ACC001/statement?fromDate=2026-03-01&toDate=2026-04-12"

Décision de Design : TransactionRepository Shared

Une décision de design importante : les deux types de comptes (BankAccount et SavingsAccount) partagent le même ITransactionRepository.

Rationalité :

  • Un client peut avoir plusieurs comptes (courant + livret)
  • Un relevé consolidé pourrait être nécessaire
  • Simplifie la persistence (une seule table/collection)

Alternative possible : Un TransactionRepository par type de compte si isolation stricte requise.

Statuts HTTP

CodeSignification
200 OKSuccès
201 CreatedRessource créée
400 Bad RequestErreur de validation
404 Not FoundRessource non trouvée
409 ConflictRessource déjà existante

CI Pipeline

GitHub Actions (.github/workflows/ci.yml)

ÉtapeDescription
CheckoutRécupération du code
Setup .NETInstallation .NET 8
RestoreRestauration des dépendances
BuildCompilation en Release
TestsTests unitaires + d'intégration

About

[C#] POC of a Banking API basic system (Hexagonal architecture)

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

BankingKata - Architecture Hexagonale

Application bancaire en architecture hexagonale (ports & adapters) avec .NET 8 et tests complets.

Architecture Hexagonale

graph TB
subgraph "Couche API (Drivers)"
API["API REST<br/>BankingKata.Api"]
end
subgraph "Couche Application (Use Cases)"
AS["BankAccountService"]
SAS["SavingsAccountService"]
end
subgraph "Couche Domaine (Core)"
BA["BankAccount"]
SA["SavingsAccount"]
T["Transaction"]
end
subgraph "Couche Infrastructure (Adapters)"
InMemRepo["InMemoryBankAccountRepository"]
InMemSavRepo["InMemorySavingsAccountRepository"]
InMemTxRepo["InMemoryTransactionRepository"]
end
subgraph "Ports (Interfaces) - Domain"
IRepo["IBankAccountRepository"]
ISavRepo["ISavingsAccountRepository"]
ITxRepo["ITransactionRepository"]
end
API --> AS
API --> SAS
AS --> BA
AS --> IRepo
AS --> ITxRepo
AS --> T
SAS --> SA
SAS --> ISavRepo
SAS --> ITxRepo
SAS --> T
IRepo -.->|implémenté par| InMemRepo
ISavRepo -.->|implémenté par| InMemSavRepo
ITxRepo -.->|implémenté par| InMemTxRepo
Loading

Flux de Données

sequenceDiagram
participant Client
participant Controller
participant Service
participant Repository
participant DB
Note over Client,DB: COMMAND (Écriture)
Client->>Controller: POST /api/accounts
Controller->>Service: CreateAccount(dto)
Service->>Domain: new BankAccount()
Domain-->>Service: account
Service->>Repository: Save(account)
Repository->>DB: INSERT
DB-->>Repository: success
Repository-->>Service: saved
Service-->>Controller: result
Controller-->>Client: 201 Created
Note over Client,DB: QUERY (Lecture)
Client->>Controller: GET /api/accounts/1
Controller->>Service: GetAccount(1)
Service->>Repository: FindById(1)
Repository->>DB: SELECT
DB-->>Repository: entity
Repository-->>Service: dto
Service-->>Controller: account
Controller-->>Client: 200 OK
Loading

Structure du Projet

BankingKata/
├── BankingKata.sln
│
├── BankingKata.Domain/ # 🟢 Core - Règles métier pures
│ ├── Entities/
│ │ ├── BankAccount.cs # Compte courant
│ │ ├── SavingsAccount.cs # Livret d'épargne
│ │ └── Transaction.cs # Opération
│ └── Ports/ # Interfaces (contrats métier)
│ ├── IBankAccountRepository.cs
│ ├── ISavingsAccountRepository.cs
│ └── ITransactionRepository.cs
│
├── BankingKata.Application/ # 🟡 Use Cases
│ ├── DTOs/ # Data Transfer Objects
│ │ ├── BankAccountDto.cs
│ │ ├── SavingsAccountDto.cs
│ │ └── StatementDto.cs
│ └── UseCases/ # Logique applicative
│ ├── AccountServices.cs # BankAccountService + SavingsAccountService unifiés
│
├── BankingKata.Infrastructure/ # 🔵 Adapters - Implémentations
│ └── Persistence/
│ ├── InMemoryBankAccountRepository.cs
│ ├── InMemorySavingsAccountRepository.cs
│ └── InMemoryTransactionRepository.cs
│
├── BankingKata.Api/ # 🚀 API REST
│ ├── Controllers/
│ │ ├── AccountsController.cs
│ │ └── SavingsController.cs
│ ├── Program.cs
│ └── Properties/
│
├── BankingKata.Tests/ # 🧪 Tests Unitaires
│ ├── BankAccountTests.cs
│ ├── BankAccountServiceTests.cs
│ ├── SavingsAccountTests.cs
│ └── SavingsAccountServiceTests.cs
│
├── BankingKata.Api.Tests/ # 🧪 Tests d'Intégration
│ ├── AccountsControllerTests.cs
│ └── SavingsControllerTests.cs
│
└── .github/workflows/
└── ci.yml # Pipeline CI

Principes de l'Architecture Hexagonale

PrincipeImplémentation
Indépendance du domaineBankingKata.Domain n'a aucune dépendance externe
Dependency RuleLe Domain définit les ports; les couches extérieures dépendent du Domain
Ports (interfaces)Dans Domain/Ports/ : IBankAccountRepository, ITransactionRepository
AdaptersImplémentations concrètes dans Infrastructure/Persistence/
Use CasesBankAccountService, SavingsAccountService dans Application/UseCases/
Injection de dépendances.NET DI container dans Program.cs

Fonctionnalités

Feature 1 : Compte Bancaire

Compte courant avec dépôt et retrait.

FonctionnalitéDescription
Numéro de compteIdentifiant unique
SoldeMontant actuel
DépôtAjout d'argent
RetraitRetrait avec vérification du solde

Règle métier : Un retrait ne peut pas dépasser le solde disponible.

Feature 2 : Découvert Autorisé

Extension du compte courant avec une autorisation de découvert.

FonctionnalitéDescription
OverdraftLimitMontant maximum du découvert
Retrait étenduAutorisé jusqu'à solde + découvert

Règle métier : Un retrait est autorisé si montant ≤ solde + autorisation_decouvert.

Feature 3 : Livret d'Épargne

Compte avec plafond de dépôt, sans découvert possible.

FonctionnalitéDescription
DepositCeilingPlafond maximum de dépôt
Dépôt limitéVérification du plafond
Pas de découvertRetrait limité au solde

Règle métier : Un dépôt ne peut pas dépasser le plafond du livret.

Feature 4 : Relevé de Compte

Historique des opérations sur un mois glissant.

FonctionnalitéDescription
Type de compte"Compte Courant" ou "Livret"
Solde actuelBalance à la date d'émission
OpérationsListe triée antéchronologique

API Endpoints

Comptes Courants

MéthodeEndpointDescriptionCorps
GET/api/accountsListe tous les comptes-
GET/api/accounts/{accountNumber}Récupère un compte-
POST/api/accountsCrée un compteCreateAccountDto
POST/api/accounts/{accountNumber}/depositDépôtTransactionDto
POST/api/accounts/{accountNumber}/withdrawRetraitTransactionDto
PATCH/api/accounts/{accountNumber}/overdraftModifie le découvertSetOverdraftDto
GET/api/accounts/{accountNumber}/statementRelevé de compteQuery params: fromDate, toDate

Livrets d'Épargne

MéthodeEndpointDescriptionCorps
GET/api/savingsListe tous les livrets-
GET/api/savings/{accountNumber}Récupère un livret-
POST/api/savingsCrée un livretCreateSavingsAccountDto
POST/api/savings/{accountNumber}/depositDépôtSavingsTransactionDto
POST/api/savings/{accountNumber}/withdrawRetraitSavingsTransactionDto
GET/api/savings/{accountNumber}/statementRelevé de livretQuery params: fromDate, toDate

DTOs (Data Transfer Objects)

BankAccountDto

{
"accountNumber": "ACC001",
"balance": 1000.00,
"overdraftLimit": 500.00
}

CreateAccountDto

{
"accountNumber": "ACC001",
"initialBalance": 1000.00,
"overdraftLimit": 500.00
}

SavingsAccountDto

{
"accountNumber": "SAV001",
"balance": 5000.00,
"depositCeiling": 22950.00
}

CreateSavingsAccountDto

{
"accountNumber": "SAV001",
"depositCeiling": 22950.00,
"initialBalance": 1000.00
}

StatementDto (Relevé)

{
"accountNumber": "ACC001",
"accountType": "Compte Courant",
"currentBalance": 1200.00,
"statementDate": "2026-04-12T12:00:00Z",
"operations": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"accountNumber": "ACC001",
"amount": 500.00,
"type": "Deposit",
"date": "2026-04-12T11:30:00Z",
"balanceAfterTransaction": 1500.00
}
]
}

Installation et Lancement

Prérequis

  • .NET 8.0 SDK
  • (Optionnel) Node.js pour le frontend React

Lancer l'API

cd BankingKata/BankingKata.Api
dotnet run

L'API sera disponible sur http://0.0.0.0:5000

Swagger UI accessible sur http://0.0.0.0:5000/swagger

Lancer les Tests

dotnet test

Structure des Tests

ProjetTypeCouverture
BankingKata.TestsUnitairesDomain + Application
BankingKata.Api.TestsIntégrationAPI REST

Exemples d'Utilisation

Créer un compte courant avec découvert

curl -X POST http://localhost:5000/api/accounts \
-H "Content-Type: application/json" \
-d '{"accountNumber": "ACC001", "initialBalance": 1000, "overdraftLimit": 500}'

Effectuer un dépôt

curl -X POST http://localhost:5000/api/accounts/ACC001/deposit \
-H "Content-Type: application/json" \
-d '{"amount": 250}'

Effectuer un retrait (avec découvert)

curl -X POST http://localhost:5000/api/accounts/ACC001/withdraw \
-H "Content-Type: application/json" \
-d '{"amount": 1200}'

Créer un livret d'épargne

curl -X POST http://localhost:5000/api/savings \
-H "Content-Type: application/json" \
-d '{"accountNumber": "SAV001", "depositCeiling": 22950, "initialBalance": 5000}'

Obtenir un relevé

curl "http://localhost:5000/api/accounts/ACC001/statement"

Obtenir un relevé sur une période

curl "http://localhost:5000/api/accounts/ACC001/statement?fromDate=2026-03-01&toDate=2026-04-12"

Décision de Design : TransactionRepository Shared

Une décision de design importante : les deux types de comptes (BankAccount et SavingsAccount) partagent le même ITransactionRepository.

Rationalité :

  • Un client peut avoir plusieurs comptes (courant + livret)
  • Un relevé consolidé pourrait être nécessaire
  • Simplifie la persistence (une seule table/collection)

Alternative possible : Un TransactionRepository par type de compte si isolation stricte requise.

Statuts HTTP

CodeSignification
200 OKSuccès
201 CreatedRessource créée
400 Bad RequestErreur de validation
404 Not FoundRessource non trouvée
409 ConflictRessource déjà existante

CI Pipeline

GitHub Actions (.github/workflows/ci.yml)

ÉtapeDescription
CheckoutRécupération du code
Setup .NETInstallation .NET 8
RestoreRestauration des dépendances
BuildCompilation en Release
TestsTests unitaires + d'intégration

About

[C#] POC of a Banking API basic system (Hexagonal architecture)

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

BankingKata - Architecture Hexagonale

Application bancaire en architecture hexagonale (ports & adapters) avec .NET 8 et tests complets.

Architecture Hexagonale

graph TB
subgraph "Couche API (Drivers)"
API["API REST<br/>BankingKata.Api"]
end
subgraph "Couche Application (Use Cases)"
AS["BankAccountService"]
SAS["SavingsAccountService"]
end
subgraph "Couche Domaine (Core)"
BA["BankAccount"]
SA["SavingsAccount"]
T["Transaction"]
end
subgraph "Couche Infrastructure (Adapters)"
InMemRepo["InMemoryBankAccountRepository"]
InMemSavRepo["InMemorySavingsAccountRepository"]
InMemTxRepo["InMemoryTransactionRepository"]
end
subgraph "Ports (Interfaces) - Domain"
IRepo["IBankAccountRepository"]
ISavRepo["ISavingsAccountRepository"]
ITxRepo["ITransactionRepository"]
end
API --> AS
API --> SAS
AS --> BA
AS --> IRepo
AS --> ITxRepo
AS --> T
SAS --> SA
SAS --> ISavRepo
SAS --> ITxRepo
SAS --> T
IRepo -.->|implémenté par| InMemRepo
ISavRepo -.->|implémenté par| InMemSavRepo
ITxRepo -.->|implémenté par| InMemTxRepo
Loading

Flux de Données

sequenceDiagram
participant Client
participant Controller
participant Service
participant Repository
participant DB
Note over Client,DB: COMMAND (Écriture)
Client->>Controller: POST /api/accounts
Controller->>Service: CreateAccount(dto)
Service->>Domain: new BankAccount()
Domain-->>Service: account
Service->>Repository: Save(account)
Repository->>DB: INSERT
DB-->>Repository: success
Repository-->>Service: saved
Service-->>Controller: result
Controller-->>Client: 201 Created
Note over Client,DB: QUERY (Lecture)
Client->>Controller: GET /api/accounts/1
Controller->>Service: GetAccount(1)
Service->>Repository: FindById(1)
Repository->>DB: SELECT
DB-->>Repository: entity
Repository-->>Service: dto
Service-->>Controller: account
Controller-->>Client: 200 OK
Loading

Structure du Projet

BankingKata/
├── BankingKata.sln
│
├── BankingKata.Domain/ # 🟢 Core - Règles métier pures
│ ├── Entities/
│ │ ├── BankAccount.cs # Compte courant
│ │ ├── SavingsAccount.cs # Livret d'épargne
│ │ └── Transaction.cs # Opération
│ └── Ports/ # Interfaces (contrats métier)
│ ├── IBankAccountRepository.cs
│ ├── ISavingsAccountRepository.cs
│ └── ITransactionRepository.cs
│
├── BankingKata.Application/ # 🟡 Use Cases
│ ├── DTOs/ # Data Transfer Objects
│ │ ├── BankAccountDto.cs
│ │ ├── SavingsAccountDto.cs
│ │ └── StatementDto.cs
│ └── UseCases/ # Logique applicative
│ ├── AccountServices.cs # BankAccountService + SavingsAccountService unifiés
│
├── BankingKata.Infrastructure/ # 🔵 Adapters - Implémentations
│ └── Persistence/
│ ├── InMemoryBankAccountRepository.cs
│ ├── InMemorySavingsAccountRepository.cs
│ └── InMemoryTransactionRepository.cs
│
├── BankingKata.Api/ # 🚀 API REST
│ ├── Controllers/
│ │ ├── AccountsController.cs
│ │ └── SavingsController.cs
│ ├── Program.cs
│ └── Properties/
│
├── BankingKata.Tests/ # 🧪 Tests Unitaires
│ ├── BankAccountTests.cs
│ ├── BankAccountServiceTests.cs
│ ├── SavingsAccountTests.cs
│ └── SavingsAccountServiceTests.cs
│
├── BankingKata.Api.Tests/ # 🧪 Tests d'Intégration
│ ├── AccountsControllerTests.cs
│ └── SavingsControllerTests.cs
│
└── .github/workflows/
└── ci.yml # Pipeline CI

Principes de l'Architecture Hexagonale

PrincipeImplémentation
Indépendance du domaineBankingKata.Domain n'a aucune dépendance externe
Dependency RuleLe Domain définit les ports; les couches extérieures dépendent du Domain
Ports (interfaces)Dans Domain/Ports/ : IBankAccountRepository, ITransactionRepository
AdaptersImplémentations concrètes dans Infrastructure/Persistence/
Use CasesBankAccountService, SavingsAccountService dans Application/UseCases/
Injection de dépendances.NET DI container dans Program.cs

Fonctionnalités

Feature 1 : Compte Bancaire

Compte courant avec dépôt et retrait.

FonctionnalitéDescription
Numéro de compteIdentifiant unique
SoldeMontant actuel
DépôtAjout d'argent
RetraitRetrait avec vérification du solde

Règle métier : Un retrait ne peut pas dépasser le solde disponible.

Feature 2 : Découvert Autorisé

Extension du compte courant avec une autorisation de découvert.

FonctionnalitéDescription
OverdraftLimitMontant maximum du découvert
Retrait étenduAutorisé jusqu'à solde + découvert

Règle métier : Un retrait est autorisé si montant ≤ solde + autorisation_decouvert.

Feature 3 : Livret d'Épargne

Compte avec plafond de dépôt, sans découvert possible.

FonctionnalitéDescription
DepositCeilingPlafond maximum de dépôt
Dépôt limitéVérification du plafond
Pas de découvertRetrait limité au solde

Règle métier : Un dépôt ne peut pas dépasser le plafond du livret.

Feature 4 : Relevé de Compte

Historique des opérations sur un mois glissant.

FonctionnalitéDescription
Type de compte"Compte Courant" ou "Livret"
Solde actuelBalance à la date d'émission
OpérationsListe triée antéchronologique

API Endpoints

Comptes Courants

MéthodeEndpointDescriptionCorps
GET/api/accountsListe tous les comptes-
GET/api/accounts/{accountNumber}Récupère un compte-
POST/api/accountsCrée un compteCreateAccountDto
POST/api/accounts/{accountNumber}/depositDépôtTransactionDto
POST/api/accounts/{accountNumber}/withdrawRetraitTransactionDto
PATCH/api/accounts/{accountNumber}/overdraftModifie le découvertSetOverdraftDto
GET/api/accounts/{accountNumber}/statementRelevé de compteQuery params: fromDate, toDate

Livrets d'Épargne

MéthodeEndpointDescriptionCorps
GET/api/savingsListe tous les livrets-
GET/api/savings/{accountNumber}Récupère un livret-
POST/api/savingsCrée un livretCreateSavingsAccountDto
POST/api/savings/{accountNumber}/depositDépôtSavingsTransactionDto
POST/api/savings/{accountNumber}/withdrawRetraitSavingsTransactionDto
GET/api/savings/{accountNumber}/statementRelevé de livretQuery params: fromDate, toDate

DTOs (Data Transfer Objects)

BankAccountDto

{
"accountNumber": "ACC001",
"balance": 1000.00,
"overdraftLimit": 500.00
}

CreateAccountDto

{
"accountNumber": "ACC001",
"initialBalance": 1000.00,
"overdraftLimit": 500.00
}

SavingsAccountDto

{
"accountNumber": "SAV001",
"balance": 5000.00,
"depositCeiling": 22950.00
}

CreateSavingsAccountDto

{
"accountNumber": "SAV001",
"depositCeiling": 22950.00,
"initialBalance": 1000.00
}

StatementDto (Relevé)

{
"accountNumber": "ACC001",
"accountType": "Compte Courant",
"currentBalance": 1200.00,
"statementDate": "2026-04-12T12:00:00Z",
"operations": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"accountNumber": "ACC001",
"amount": 500.00,
"type": "Deposit",
"date": "2026-04-12T11:30:00Z",
"balanceAfterTransaction": 1500.00
}
]
}

Installation et Lancement

Prérequis

  • .NET 8.0 SDK
  • (Optionnel) Node.js pour le frontend React

Lancer l'API

cd BankingKata/BankingKata.Api
dotnet run

L'API sera disponible sur http://0.0.0.0:5000

Swagger UI accessible sur http://0.0.0.0:5000/swagger

Lancer les Tests

dotnet test

Structure des Tests

ProjetTypeCouverture
BankingKata.TestsUnitairesDomain + Application
BankingKata.Api.TestsIntégrationAPI REST

Exemples d'Utilisation

Créer un compte courant avec découvert

curl -X POST http://localhost:5000/api/accounts \
-H "Content-Type: application/json" \
-d '{"accountNumber": "ACC001", "initialBalance": 1000, "overdraftLimit": 500}'

Effectuer un dépôt

curl -X POST http://localhost:5000/api/accounts/ACC001/deposit \
-H "Content-Type: application/json" \
-d '{"amount": 250}'

Effectuer un retrait (avec découvert)

curl -X POST http://localhost:5000/api/accounts/ACC001/withdraw \
-H "Content-Type: application/json" \
-d '{"amount": 1200}'

Créer un livret d'épargne

curl -X POST http://localhost:5000/api/savings \
-H "Content-Type: application/json" \
-d '{"accountNumber": "SAV001", "depositCeiling": 22950, "initialBalance": 5000}'

Obtenir un relevé

curl "http://localhost:5000/api/accounts/ACC001/statement"

Obtenir un relevé sur une période

curl "http://localhost:5000/api/accounts/ACC001/statement?fromDate=2026-03-01&toDate=2026-04-12"

Décision de Design : TransactionRepository Shared

Une décision de design importante : les deux types de comptes (BankAccount et SavingsAccount) partagent le même ITransactionRepository.

Rationalité :

  • Un client peut avoir plusieurs comptes (courant + livret)
  • Un relevé consolidé pourrait être nécessaire
  • Simplifie la persistence (une seule table/collection)

Alternative possible : Un TransactionRepository par type de compte si isolation stricte requise.

Statuts HTTP

CodeSignification
200 OKSuccès
201 CreatedRessource créée
400 Bad RequestErreur de validation
404 Not FoundRessource non trouvée
409 ConflictRessource déjà existante

CI Pipeline

GitHub Actions (.github/workflows/ci.yml)

ÉtapeDescription
CheckoutRécupération du code
Setup .NETInstallation .NET 8
RestoreRestauration des dépendances
BuildCompilation en Release
TestsTests unitaires + d'intégration

About

[C#] POC of a Banking API basic system (Hexagonal architecture)

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

BankingKata - Architecture Hexagonale

Application bancaire en architecture hexagonale (ports & adapters) avec .NET 8 et tests complets.

Architecture Hexagonale

graph TB
subgraph "Couche API (Drivers)"
API["API REST<br/>BankingKata.Api"]
end
subgraph "Couche Application (Use Cases)"
AS["BankAccountService"]
SAS["SavingsAccountService"]
end
subgraph "Couche Domaine (Core)"
BA["BankAccount"]
SA["SavingsAccount"]
T["Transaction"]
end
subgraph "Couche Infrastructure (Adapters)"
InMemRepo["InMemoryBankAccountRepository"]
InMemSavRepo["InMemorySavingsAccountRepository"]
InMemTxRepo["InMemoryTransactionRepository"]
end
subgraph "Ports (Interfaces) - Domain"
IRepo["IBankAccountRepository"]
ISavRepo["ISavingsAccountRepository"]
ITxRepo["ITransactionRepository"]
end
API --> AS
API --> SAS
AS --> BA
AS --> IRepo
AS --> ITxRepo
AS --> T
SAS --> SA
SAS --> ISavRepo
SAS --> ITxRepo
SAS --> T
IRepo -.->|implémenté par| InMemRepo
ISavRepo -.->|implémenté par| InMemSavRepo
ITxRepo -.->|implémenté par| InMemTxRepo
Loading

Flux de Données

sequenceDiagram
participant Client
participant Controller
participant Service
participant Repository
participant DB
Note over Client,DB: COMMAND (Écriture)
Client->>Controller: POST /api/accounts
Controller->>Service: CreateAccount(dto)
Service->>Domain: new BankAccount()
Domain-->>Service: account
Service->>Repository: Save(account)
Repository->>DB: INSERT
DB-->>Repository: success
Repository-->>Service: saved
Service-->>Controller: result
Controller-->>Client: 201 Created
Note over Client,DB: QUERY (Lecture)
Client->>Controller: GET /api/accounts/1
Controller->>Service: GetAccount(1)
Service->>Repository: FindById(1)
Repository->>DB: SELECT
DB-->>Repository: entity
Repository-->>Service: dto
Service-->>Controller: account
Controller-->>Client: 200 OK
Loading

Structure du Projet

BankingKata/
├── BankingKata.sln
│
├── BankingKata.Domain/ # 🟢 Core - Règles métier pures
│ ├── Entities/
│ │ ├── BankAccount.cs # Compte courant
│ │ ├── SavingsAccount.cs # Livret d'épargne
│ │ └── Transaction.cs # Opération
│ └── Ports/ # Interfaces (contrats métier)
│ ├── IBankAccountRepository.cs
│ ├── ISavingsAccountRepository.cs
│ └── ITransactionRepository.cs
│
├── BankingKata.Application/ # 🟡 Use Cases
│ ├── DTOs/ # Data Transfer Objects
│ │ ├── BankAccountDto.cs
│ │ ├── SavingsAccountDto.cs
│ │ └── StatementDto.cs
│ └── UseCases/ # Logique applicative
│ ├── AccountServices.cs # BankAccountService + SavingsAccountService unifiés
│
├── BankingKata.Infrastructure/ # 🔵 Adapters - Implémentations
│ └── Persistence/
│ ├── InMemoryBankAccountRepository.cs
│ ├── InMemorySavingsAccountRepository.cs
│ └── InMemoryTransactionRepository.cs
│
├── BankingKata.Api/ # 🚀 API REST
│ ├── Controllers/
│ │ ├── AccountsController.cs
│ │ └── SavingsController.cs
│ ├── Program.cs
│ └── Properties/
│
├── BankingKata.Tests/ # 🧪 Tests Unitaires
│ ├── BankAccountTests.cs
│ ├── BankAccountServiceTests.cs
│ ├── SavingsAccountTests.cs
│ └── SavingsAccountServiceTests.cs
│
├── BankingKata.Api.Tests/ # 🧪 Tests d'Intégration
│ ├── AccountsControllerTests.cs
│ └── SavingsControllerTests.cs
│
└── .github/workflows/
└── ci.yml # Pipeline CI

Principes de l'Architecture Hexagonale

PrincipeImplémentation
Indépendance du domaineBankingKata.Domain n'a aucune dépendance externe
Dependency RuleLe Domain définit les ports; les couches extérieures dépendent du Domain
Ports (interfaces)Dans Domain/Ports/ : IBankAccountRepository, ITransactionRepository
AdaptersImplémentations concrètes dans Infrastructure/Persistence/
Use CasesBankAccountService, SavingsAccountService dans Application/UseCases/
Injection de dépendances.NET DI container dans Program.cs

Fonctionnalités

Feature 1 : Compte Bancaire

Compte courant avec dépôt et retrait.

FonctionnalitéDescription
Numéro de compteIdentifiant unique
SoldeMontant actuel
DépôtAjout d'argent
RetraitRetrait avec vérification du solde

Règle métier : Un retrait ne peut pas dépasser le solde disponible.

Feature 2 : Découvert Autorisé

Extension du compte courant avec une autorisation de découvert.

FonctionnalitéDescription
OverdraftLimitMontant maximum du découvert
Retrait étenduAutorisé jusqu'à solde + découvert

Règle métier : Un retrait est autorisé si montant ≤ solde + autorisation_decouvert.

Feature 3 : Livret d'Épargne

Compte avec plafond de dépôt, sans découvert possible.

FonctionnalitéDescription
DepositCeilingPlafond maximum de dépôt
Dépôt limitéVérification du plafond
Pas de découvertRetrait limité au solde

Règle métier : Un dépôt ne peut pas dépasser le plafond du livret.

Feature 4 : Relevé de Compte

Historique des opérations sur un mois glissant.

FonctionnalitéDescription
Type de compte"Compte Courant" ou "Livret"
Solde actuelBalance à la date d'émission
OpérationsListe triée antéchronologique

API Endpoints

Comptes Courants

MéthodeEndpointDescriptionCorps
GET/api/accountsListe tous les comptes-
GET/api/accounts/{accountNumber}Récupère un compte-
POST/api/accountsCrée un compteCreateAccountDto
POST/api/accounts/{accountNumber}/depositDépôtTransactionDto
POST/api/accounts/{accountNumber}/withdrawRetraitTransactionDto
PATCH/api/accounts/{accountNumber}/overdraftModifie le découvertSetOverdraftDto
GET/api/accounts/{accountNumber}/statementRelevé de compteQuery params: fromDate, toDate

Livrets d'Épargne

MéthodeEndpointDescriptionCorps
GET/api/savingsListe tous les livrets-
GET/api/savings/{accountNumber}Récupère un livret-
POST/api/savingsCrée un livretCreateSavingsAccountDto
POST/api/savings/{accountNumber}/depositDépôtSavingsTransactionDto
POST/api/savings/{accountNumber}/withdrawRetraitSavingsTransactionDto
GET/api/savings/{accountNumber}/statementRelevé de livretQuery params: fromDate, toDate

DTOs (Data Transfer Objects)

BankAccountDto

{
"accountNumber": "ACC001",
"balance": 1000.00,
"overdraftLimit": 500.00
}

CreateAccountDto

{
"accountNumber": "ACC001",
"initialBalance": 1000.00,
"overdraftLimit": 500.00
}

SavingsAccountDto

{
"accountNumber": "SAV001",
"balance": 5000.00,
"depositCeiling": 22950.00
}

CreateSavingsAccountDto

{
"accountNumber": "SAV001",
"depositCeiling": 22950.00,
"initialBalance": 1000.00
}

StatementDto (Relevé)

{
"accountNumber": "ACC001",
"accountType": "Compte Courant",
"currentBalance": 1200.00,
"statementDate": "2026-04-12T12:00:00Z",
"operations": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"accountNumber": "ACC001",
"amount": 500.00,
"type": "Deposit",
"date": "2026-04-12T11:30:00Z",
"balanceAfterTransaction": 1500.00
}
]
}

Installation et Lancement

Prérequis

  • .NET 8.0 SDK
  • (Optionnel) Node.js pour le frontend React

Lancer l'API

cd BankingKata/BankingKata.Api
dotnet run

L'API sera disponible sur http://0.0.0.0:5000

Swagger UI accessible sur http://0.0.0.0:5000/swagger

Lancer les Tests

dotnet test

Structure des Tests

ProjetTypeCouverture
BankingKata.TestsUnitairesDomain + Application
BankingKata.Api.TestsIntégrationAPI REST

Exemples d'Utilisation

Créer un compte courant avec découvert

curl -X POST http://localhost:5000/api/accounts \
-H "Content-Type: application/json" \
-d '{"accountNumber": "ACC001", "initialBalance": 1000, "overdraftLimit": 500}'

Effectuer un dépôt

curl -X POST http://localhost:5000/api/accounts/ACC001/deposit \
-H "Content-Type: application/json" \
-d '{"amount": 250}'

Effectuer un retrait (avec découvert)

curl -X POST http://localhost:5000/api/accounts/ACC001/withdraw \
-H "Content-Type: application/json" \
-d '{"amount": 1200}'

Créer un livret d'épargne

curl -X POST http://localhost:5000/api/savings \
-H "Content-Type: application/json" \
-d '{"accountNumber": "SAV001", "depositCeiling": 22950, "initialBalance": 5000}'

Obtenir un relevé

curl "http://localhost:5000/api/accounts/ACC001/statement"

Obtenir un relevé sur une période

curl "http://localhost:5000/api/accounts/ACC001/statement?fromDate=2026-03-01&toDate=2026-04-12"

Décision de Design : TransactionRepository Shared

Une décision de design importante : les deux types de comptes (BankAccount et SavingsAccount) partagent le même ITransactionRepository.

Rationalité :

  • Un client peut avoir plusieurs comptes (courant + livret)
  • Un relevé consolidé pourrait être nécessaire
  • Simplifie la persistence (une seule table/collection)

Alternative possible : Un TransactionRepository par type de compte si isolation stricte requise.

Statuts HTTP

CodeSignification
200 OKSuccès
201 CreatedRessource créée
400 Bad RequestErreur de validation
404 Not FoundRessource non trouvée
409 ConflictRessource déjà existante

CI Pipeline

GitHub Actions (.github/workflows/ci.yml)

ÉtapeDescription
CheckoutRécupération du code
Setup .NETInstallation .NET 8
RestoreRestauration des dépendances
BuildCompilation en Release
TestsTests unitaires + d'intégration

About

[C#] POC of a Banking API basic system (Hexagonal architecture)

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

BankingKata - Architecture Hexagonale

Application bancaire en architecture hexagonale (ports & adapters) avec .NET 8 et tests complets.

Architecture Hexagonale

graph TB
subgraph "Couche API (Drivers)"
API["API REST<br/>BankingKata.Api"]
end
subgraph "Couche Application (Use Cases)"
AS["BankAccountService"]
SAS["SavingsAccountService"]
end
subgraph "Couche Domaine (Core)"
BA["BankAccount"]
SA["SavingsAccount"]
T["Transaction"]
end
subgraph "Couche Infrastructure (Adapters)"
InMemRepo["InMemoryBankAccountRepository"]
InMemSavRepo["InMemorySavingsAccountRepository"]
InMemTxRepo["InMemoryTransactionRepository"]
end
subgraph "Ports (Interfaces) - Domain"
IRepo["IBankAccountRepository"]
ISavRepo["ISavingsAccountRepository"]
ITxRepo["ITransactionRepository"]
end
API --> AS
API --> SAS
AS --> BA
AS --> IRepo
AS --> ITxRepo
AS --> T
SAS --> SA
SAS --> ISavRepo
SAS --> ITxRepo
SAS --> T
IRepo -.->|implémenté par| InMemRepo
ISavRepo -.->|implémenté par| InMemSavRepo
ITxRepo -.->|implémenté par| InMemTxRepo
Loading

Flux de Données

sequenceDiagram
participant Client
participant Controller
participant Service
participant Repository
participant DB
Note over Client,DB: COMMAND (Écriture)
Client->>Controller: POST /api/accounts
Controller->>Service: CreateAccount(dto)
Service->>Domain: new BankAccount()
Domain-->>Service: account
Service->>Repository: Save(account)
Repository->>DB: INSERT
DB-->>Repository: success
Repository-->>Service: saved
Service-->>Controller: result
Controller-->>Client: 201 Created
Note over Client,DB: QUERY (Lecture)
Client->>Controller: GET /api/accounts/1
Controller->>Service: GetAccount(1)
Service->>Repository: FindById(1)
Repository->>DB: SELECT
DB-->>Repository: entity
Repository-->>Service: dto
Service-->>Controller: account
Controller-->>Client: 200 OK
Loading

Structure du Projet

BankingKata/
├── BankingKata.sln
│
├── BankingKata.Domain/ # 🟢 Core - Règles métier pures
│ ├── Entities/
│ │ ├── BankAccount.cs # Compte courant
│ │ ├── SavingsAccount.cs # Livret d'épargne
│ │ └── Transaction.cs # Opération
│ └── Ports/ # Interfaces (contrats métier)
│ ├── IBankAccountRepository.cs
│ ├── ISavingsAccountRepository.cs
│ └── ITransactionRepository.cs
│
├── BankingKata.Application/ # 🟡 Use Cases
│ ├── DTOs/ # Data Transfer Objects
│ │ ├── BankAccountDto.cs
│ │ ├── SavingsAccountDto.cs
│ │ └── StatementDto.cs
│ └── UseCases/ # Logique applicative
│ ├── AccountServices.cs # BankAccountService + SavingsAccountService unifiés
│
├── BankingKata.Infrastructure/ # 🔵 Adapters - Implémentations
│ └── Persistence/
│ ├── InMemoryBankAccountRepository.cs
│ ├── InMemorySavingsAccountRepository.cs
│ └── InMemoryTransactionRepository.cs
│
├── BankingKata.Api/ # 🚀 API REST
│ ├── Controllers/
│ │ ├── AccountsController.cs
│ │ └── SavingsController.cs
│ ├── Program.cs
│ └── Properties/
│
├── BankingKata.Tests/ # 🧪 Tests Unitaires
│ ├── BankAccountTests.cs
│ ├── BankAccountServiceTests.cs
│ ├── SavingsAccountTests.cs
│ └── SavingsAccountServiceTests.cs
│
├── BankingKata.Api.Tests/ # 🧪 Tests d'Intégration
│ ├── AccountsControllerTests.cs
│ └── SavingsControllerTests.cs
│
└── .github/workflows/
└── ci.yml # Pipeline CI

Principes de l'Architecture Hexagonale

PrincipeImplémentation
Indépendance du domaineBankingKata.Domain n'a aucune dépendance externe
Dependency RuleLe Domain définit les ports; les couches extérieures dépendent du Domain
Ports (interfaces)Dans Domain/Ports/ : IBankAccountRepository, ITransactionRepository
AdaptersImplémentations concrètes dans Infrastructure/Persistence/
Use CasesBankAccountService, SavingsAccountService dans Application/UseCases/
Injection de dépendances.NET DI container dans Program.cs

Fonctionnalités

Feature 1 : Compte Bancaire

Compte courant avec dépôt et retrait.

FonctionnalitéDescription
Numéro de compteIdentifiant unique
SoldeMontant actuel
DépôtAjout d'argent
RetraitRetrait avec vérification du solde

Règle métier : Un retrait ne peut pas dépasser le solde disponible.

Feature 2 : Découvert Autorisé

Extension du compte courant avec une autorisation de découvert.

FonctionnalitéDescription
OverdraftLimitMontant maximum du découvert
Retrait étenduAutorisé jusqu'à solde + découvert

Règle métier : Un retrait est autorisé si montant ≤ solde + autorisation_decouvert.

Feature 3 : Livret d'Épargne

Compte avec plafond de dépôt, sans découvert possible.

FonctionnalitéDescription
DepositCeilingPlafond maximum de dépôt
Dépôt limitéVérification du plafond
Pas de découvertRetrait limité au solde

Règle métier : Un dépôt ne peut pas dépasser le plafond du livret.

Feature 4 : Relevé de Compte

Historique des opérations sur un mois glissant.

FonctionnalitéDescription
Type de compte"Compte Courant" ou "Livret"
Solde actuelBalance à la date d'émission
OpérationsListe triée antéchronologique

API Endpoints

Comptes Courants

MéthodeEndpointDescriptionCorps
GET/api/accountsListe tous les comptes-
GET/api/accounts/{accountNumber}Récupère un compte-
POST/api/accountsCrée un compteCreateAccountDto
POST/api/accounts/{accountNumber}/depositDépôtTransactionDto
POST/api/accounts/{accountNumber}/withdrawRetraitTransactionDto
PATCH/api/accounts/{accountNumber}/overdraftModifie le découvertSetOverdraftDto
GET/api/accounts/{accountNumber}/statementRelevé de compteQuery params: fromDate, toDate

Livrets d'Épargne

MéthodeEndpointDescriptionCorps
GET/api/savingsListe tous les livrets-
GET/api/savings/{accountNumber}Récupère un livret-
POST/api/savingsCrée un livretCreateSavingsAccountDto
POST/api/savings/{accountNumber}/depositDépôtSavingsTransactionDto
POST/api/savings/{accountNumber}/withdrawRetraitSavingsTransactionDto
GET/api/savings/{accountNumber}/statementRelevé de livretQuery params: fromDate, toDate

DTOs (Data Transfer Objects)

BankAccountDto

{
"accountNumber": "ACC001",
"balance": 1000.00,
"overdraftLimit": 500.00
}

CreateAccountDto

{
"accountNumber": "ACC001",
"initialBalance": 1000.00,
"overdraftLimit": 500.00
}

SavingsAccountDto

{
"accountNumber": "SAV001",
"balance": 5000.00,
"depositCeiling": 22950.00
}

CreateSavingsAccountDto

{
"accountNumber": "SAV001",
"depositCeiling": 22950.00,
"initialBalance": 1000.00
}

StatementDto (Relevé)

{
"accountNumber": "ACC001",
"accountType": "Compte Courant",
"currentBalance": 1200.00,
"statementDate": "2026-04-12T12:00:00Z",
"operations": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"accountNumber": "ACC001",
"amount": 500.00,
"type": "Deposit",
"date": "2026-04-12T11:30:00Z",
"balanceAfterTransaction": 1500.00
}
]
}

Installation et Lancement

Prérequis

  • .NET 8.0 SDK
  • (Optionnel) Node.js pour le frontend React

Lancer l'API

cd BankingKata/BankingKata.Api
dotnet run

L'API sera disponible sur http://0.0.0.0:5000

Swagger UI accessible sur http://0.0.0.0:5000/swagger

Lancer les Tests

dotnet test

Structure des Tests

ProjetTypeCouverture
BankingKata.TestsUnitairesDomain + Application
BankingKata.Api.TestsIntégrationAPI REST

Exemples d'Utilisation

Créer un compte courant avec découvert

curl -X POST http://localhost:5000/api/accounts \
-H "Content-Type: application/json" \
-d '{"accountNumber": "ACC001", "initialBalance": 1000, "overdraftLimit": 500}'

Effectuer un dépôt

curl -X POST http://localhost:5000/api/accounts/ACC001/deposit \
-H "Content-Type: application/json" \
-d '{"amount": 250}'

Effectuer un retrait (avec découvert)

curl -X POST http://localhost:5000/api/accounts/ACC001/withdraw \
-H "Content-Type: application/json" \
-d '{"amount": 1200}'

Créer un livret d'épargne

curl -X POST http://localhost:5000/api/savings \
-H "Content-Type: application/json" \
-d '{"accountNumber": "SAV001", "depositCeiling": 22950, "initialBalance": 5000}'

Obtenir un relevé

curl "http://localhost:5000/api/accounts/ACC001/statement"

Obtenir un relevé sur une période

curl "http://localhost:5000/api/accounts/ACC001/statement?fromDate=2026-03-01&toDate=2026-04-12"

Décision de Design : TransactionRepository Shared

Une décision de design importante : les deux types de comptes (BankAccount et SavingsAccount) partagent le même ITransactionRepository.

Rationalité :

  • Un client peut avoir plusieurs comptes (courant + livret)
  • Un relevé consolidé pourrait être nécessaire
  • Simplifie la persistence (une seule table/collection)

Alternative possible : Un TransactionRepository par type de compte si isolation stricte requise.

Statuts HTTP

CodeSignification
200 OKSuccès
201 CreatedRessource créée
400 Bad RequestErreur de validation
404 Not FoundRessource non trouvée
409 ConflictRessource déjà existante

CI Pipeline

GitHub Actions (.github/workflows/ci.yml)

ÉtapeDescription
CheckoutRécupération du code
Setup .NETInstallation .NET 8
RestoreRestauration des dépendances
BuildCompilation en Release
TestsTests unitaires + d'intégration

About

[C#] POC of a Banking API basic system (Hexagonal architecture)

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

BankingKata - Architecture Hexagonale

Application bancaire en architecture hexagonale (ports & adapters) avec .NET 8 et tests complets.

Architecture Hexagonale

graph TB
subgraph "Couche API (Drivers)"
API["API REST<br/>BankingKata.Api"]
end
subgraph "Couche Application (Use Cases)"
AS["BankAccountService"]
SAS["SavingsAccountService"]
end
subgraph "Couche Domaine (Core)"
BA["BankAccount"]
SA["SavingsAccount"]
T["Transaction"]
end
subgraph "Couche Infrastructure (Adapters)"
InMemRepo["InMemoryBankAccountRepository"]
InMemSavRepo["InMemorySavingsAccountRepository"]
InMemTxRepo["InMemoryTransactionRepository"]
end
subgraph "Ports (Interfaces) - Domain"
IRepo["IBankAccountRepository"]
ISavRepo["ISavingsAccountRepository"]
ITxRepo["ITransactionRepository"]
end
API --> AS
API --> SAS
AS --> BA
AS --> IRepo
AS --> ITxRepo
AS --> T
SAS --> SA
SAS --> ISavRepo
SAS --> ITxRepo
SAS --> T
IRepo -.->|implémenté par| InMemRepo
ISavRepo -.->|implémenté par| InMemSavRepo
ITxRepo -.->|implémenté par| InMemTxRepo
Loading

Flux de Données

sequenceDiagram
participant Client
participant Controller
participant Service
participant Repository
participant DB
Note over Client,DB: COMMAND (Écriture)
Client->>Controller: POST /api/accounts
Controller->>Service: CreateAccount(dto)
Service->>Domain: new BankAccount()
Domain-->>Service: account
Service->>Repository: Save(account)
Repository->>DB: INSERT
DB-->>Repository: success
Repository-->>Service: saved
Service-->>Controller: result
Controller-->>Client: 201 Created
Note over Client,DB: QUERY (Lecture)
Client->>Controller: GET /api/accounts/1
Controller->>Service: GetAccount(1)
Service->>Repository: FindById(1)
Repository->>DB: SELECT
DB-->>Repository: entity
Repository-->>Service: dto
Service-->>Controller: account
Controller-->>Client: 200 OK
Loading

Structure du Projet

BankingKata/
├── BankingKata.sln
│
├── BankingKata.Domain/ # 🟢 Core - Règles métier pures
│ ├── Entities/
│ │ ├── BankAccount.cs # Compte courant
│ │ ├── SavingsAccount.cs # Livret d'épargne
│ │ └── Transaction.cs # Opération
│ └── Ports/ # Interfaces (contrats métier)
│ ├── IBankAccountRepository.cs
│ ├── ISavingsAccountRepository.cs
│ └── ITransactionRepository.cs
│
├── BankingKata.Application/ # 🟡 Use Cases
│ ├── DTOs/ # Data Transfer Objects
│ │ ├── BankAccountDto.cs
│ │ ├── SavingsAccountDto.cs
│ │ └── StatementDto.cs
│ └── UseCases/ # Logique applicative
│ ├── AccountServices.cs # BankAccountService + SavingsAccountService unifiés
│
├── BankingKata.Infrastructure/ # 🔵 Adapters - Implémentations
│ └── Persistence/
│ ├── InMemoryBankAccountRepository.cs
│ ├── InMemorySavingsAccountRepository.cs
│ └── InMemoryTransactionRepository.cs
│
├── BankingKata.Api/ # 🚀 API REST
│ ├── Controllers/
│ │ ├── AccountsController.cs
│ │ └── SavingsController.cs
│ ├── Program.cs
│ └── Properties/
│
├── BankingKata.Tests/ # 🧪 Tests Unitaires
│ ├── BankAccountTests.cs
│ ├── BankAccountServiceTests.cs
│ ├── SavingsAccountTests.cs
│ └── SavingsAccountServiceTests.cs
│
├── BankingKata.Api.Tests/ # 🧪 Tests d'Intégration
│ ├── AccountsControllerTests.cs
│ └── SavingsControllerTests.cs
│
└── .github/workflows/
└── ci.yml # Pipeline CI

Principes de l'Architecture Hexagonale

PrincipeImplémentation
Indépendance du domaineBankingKata.Domain n'a aucune dépendance externe
Dependency RuleLe Domain définit les ports; les couches extérieures dépendent du Domain
Ports (interfaces)Dans Domain/Ports/ : IBankAccountRepository, ITransactionRepository
AdaptersImplémentations concrètes dans Infrastructure/Persistence/
Use CasesBankAccountService, SavingsAccountService dans Application/UseCases/
Injection de dépendances.NET DI container dans Program.cs

Fonctionnalités

Feature 1 : Compte Bancaire

Compte courant avec dépôt et retrait.

FonctionnalitéDescription
Numéro de compteIdentifiant unique
SoldeMontant actuel
DépôtAjout d'argent
RetraitRetrait avec vérification du solde

Règle métier : Un retrait ne peut pas dépasser le solde disponible.

Feature 2 : Découvert Autorisé

Extension du compte courant avec une autorisation de découvert.

FonctionnalitéDescription
OverdraftLimitMontant maximum du découvert
Retrait étenduAutorisé jusqu'à solde + découvert

Règle métier : Un retrait est autorisé si montant ≤ solde + autorisation_decouvert.

Feature 3 : Livret d'Épargne

Compte avec plafond de dépôt, sans découvert possible.

FonctionnalitéDescription
DepositCeilingPlafond maximum de dépôt
Dépôt limitéVérification du plafond
Pas de découvertRetrait limité au solde

Règle métier : Un dépôt ne peut pas dépasser le plafond du livret.

Feature 4 : Relevé de Compte

Historique des opérations sur un mois glissant.

FonctionnalitéDescription
Type de compte"Compte Courant" ou "Livret"
Solde actuelBalance à la date d'émission
OpérationsListe triée antéchronologique

API Endpoints

Comptes Courants

MéthodeEndpointDescriptionCorps
GET/api/accountsListe tous les comptes-
GET/api/accounts/{accountNumber}Récupère un compte-
POST/api/accountsCrée un compteCreateAccountDto
POST/api/accounts/{accountNumber}/depositDépôtTransactionDto
POST/api/accounts/{accountNumber}/withdrawRetraitTransactionDto
PATCH/api/accounts/{accountNumber}/overdraftModifie le découvertSetOverdraftDto
GET/api/accounts/{accountNumber}/statementRelevé de compteQuery params: fromDate, toDate

Livrets d'Épargne

MéthodeEndpointDescriptionCorps
GET/api/savingsListe tous les livrets-
GET/api/savings/{accountNumber}Récupère un livret-
POST/api/savingsCrée un livretCreateSavingsAccountDto
POST/api/savings/{accountNumber}/depositDépôtSavingsTransactionDto
POST/api/savings/{accountNumber}/withdrawRetraitSavingsTransactionDto
GET/api/savings/{accountNumber}/statementRelevé de livretQuery params: fromDate, toDate

DTOs (Data Transfer Objects)

BankAccountDto

{
"accountNumber": "ACC001",
"balance": 1000.00,
"overdraftLimit": 500.00
}

CreateAccountDto

{
"accountNumber": "ACC001",
"initialBalance": 1000.00,
"overdraftLimit": 500.00
}

SavingsAccountDto

{
"accountNumber": "SAV001",
"balance": 5000.00,
"depositCeiling": 22950.00
}

CreateSavingsAccountDto

{
"accountNumber": "SAV001",
"depositCeiling": 22950.00,
"initialBalance": 1000.00
}

StatementDto (Relevé)

{
"accountNumber": "ACC001",
"accountType": "Compte Courant",
"currentBalance": 1200.00,
"statementDate": "2026-04-12T12:00:00Z",
"operations": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"accountNumber": "ACC001",
"amount": 500.00,
"type": "Deposit",
"date": "2026-04-12T11:30:00Z",
"balanceAfterTransaction": 1500.00
}
]
}

Installation et Lancement

Prérequis

  • .NET 8.0 SDK
  • (Optionnel) Node.js pour le frontend React

Lancer l'API

cd BankingKata/BankingKata.Api
dotnet run

L'API sera disponible sur http://0.0.0.0:5000

Swagger UI accessible sur http://0.0.0.0:5000/swagger

Lancer les Tests

dotnet test

Structure des Tests

ProjetTypeCouverture
BankingKata.TestsUnitairesDomain + Application
BankingKata.Api.TestsIntégrationAPI REST

Exemples d'Utilisation

Créer un compte courant avec découvert

curl -X POST http://localhost:5000/api/accounts \
-H "Content-Type: application/json" \
-d '{"accountNumber": "ACC001", "initialBalance": 1000, "overdraftLimit": 500}'

Effectuer un dépôt

curl -X POST http://localhost:5000/api/accounts/ACC001/deposit \
-H "Content-Type: application/json" \
-d '{"amount": 250}'

Effectuer un retrait (avec découvert)

curl -X POST http://localhost:5000/api/accounts/ACC001/withdraw \
-H "Content-Type: application/json" \
-d '{"amount": 1200}'

Créer un livret d'épargne

curl -X POST http://localhost:5000/api/savings \
-H "Content-Type: application/json" \
-d '{"accountNumber": "SAV001", "depositCeiling": 22950, "initialBalance": 5000}'

Obtenir un relevé

curl "http://localhost:5000/api/accounts/ACC001/statement"

Obtenir un relevé sur une période

curl "http://localhost:5000/api/accounts/ACC001/statement?fromDate=2026-03-01&toDate=2026-04-12"

Décision de Design : TransactionRepository Shared

Une décision de design importante : les deux types de comptes (BankAccount et SavingsAccount) partagent le même ITransactionRepository.

Rationalité :

  • Un client peut avoir plusieurs comptes (courant + livret)
  • Un relevé consolidé pourrait être nécessaire
  • Simplifie la persistence (une seule table/collection)

Alternative possible : Un TransactionRepository par type de compte si isolation stricte requise.

Statuts HTTP

CodeSignification
200 OKSuccès
201 CreatedRessource créée
400 Bad RequestErreur de validation
404 Not FoundRessource non trouvée
409 ConflictRessource déjà existante

CI Pipeline

GitHub Actions (.github/workflows/ci.yml)

ÉtapeDescription
CheckoutRécupération du code
Setup .NETInstallation .NET 8
RestoreRestauration des dépendances
BuildCompilation en Release
TestsTests unitaires + d'intégration

About

[C#] POC of a Banking API basic system (Hexagonal architecture)

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages