Carte Context Mapping #4 : Shared Kernel
📇 Carte #4 : Shared Kernel
Vue Rapide
🎯 Objectif : Deux équipes partagent du code et un modèle commun
👥 Relation d’équipe : Mutuellement Dépendant
📊 Couplage : Très haut (code partagé)
Concept
Deux contextes partagent du code source et un modèle de domaine commun.
Shared Kernel (Code + Model)
↙ ↘
Team A Team B
Le noyau partagé n’appartient à aucun contexte, il est gardé par les deux.
Quand l’Utiliser ? ✅
- ✅ Deux équipes très dépendantes l’une de l’autre
- ✅ Créer une interface prendrait plus de temps qu’un code partagé
- ✅ Les équipes veulent vraiment collaborer
- ✅ Le code partagé est stable et bien défini
- ✅ C’est une zone de frontière claire (pas du fouilli)
Exemples
- Domain Entities partagées : Customer, Product (tous les services en ont besoin)
- Value Objects : Money, EmailAddress (immuables, réutilisables)
- Shared Policies : Règles métier communes
- Shared Algorithms : Calculs complexes partagés
Quand l’Éviter ? ❌
- ❌ Les équipes ne se font pas confiance
- ❌ Le code partagé change fréquemment
- ❌ Vous pouvez le dupliquer (plus simple)
- ❌ Le code partagé n’est pas vraiment partagé (distinct par contexte)
- ❌ Les équipes sont géographiquement éloignées (communication difficile)
Questions Clés à se Poser 💭
- Qu’est-ce qui est vraiment partagé ? (vs. similaire)
- Comment gérons-nous les changements du noyau ?
- Qui est responsable de maintenir le Shared Kernel ?
- Qu’arrive-t-il si les équipes divergent dans leurs besoins ?
- Comment versionnons-nous ce code partagé ?
Implications pour les Deux Équipes
Responsabilités Communes
- ✓ Maintenir ensemble le Shared Kernel
- ✓ Tester à fond : les changements affectent tous les contextes
- ✓ Documenter clairement : quoi et pourquoi c’est partagé
- ✓ Communiquer avant changement
Avantages
- ✓ Efficacité : pas de duplication ou traduction
- ✓ Cohérence : une seule source de vérité
- ✓ Collaboration : force les équipes à travailler ensemble
- ✓ Évolution facile : tout le monde évolue en même temps
Risques
- ⚠️ Couplage très fort : changement = impacte les 2 contextes
- ⚠️ Synchronisation lente : besoin de coordination
- ⚠️ Conflits : si les besoins divergent
- ⚠️ Cycles de dépendance : chaque équipe attend l’autre
Structure du Shared Kernel
Option 1 : Bibliothèque Partagée
shared-kernel/ (repository/package dédié)
├── domain-entities/
│ ├── Customer.ts
│ └── Product.ts
├── value-objects/
│ ├── Money.ts
│ └── EmailAddress.ts
└── policies/
└── PricingPolicy.ts
Team A → imports shared-kernel
Team B → imports shared-kernel
Option 2 : Code Partagé dans Sous-Dossier
monorepo/
├── services/team-a/
│ ├── src/
│ └── shared/ (lien vers shared-kernel)
├── services/team-b/
│ ├── src/
│ └── shared/ (lien vers shared-kernel)
└── shared-kernel/
├── entities/
├── value-objects/
└── policies/
Exemple Concret : E-commerce Shared Entities
Contexte
Deux équipes : Catalog et Inventory
Elles partagent naturellement Product et Money
Shared Kernel
// shared-kernel/value-objects/Money.ts
export class Money {
constructor(
readonly amount: number,
readonly currency: "USD" | "EUR" | "GBP"
) {
if (amount < 0) throw new Error("Amount must be positive")
}
add(other: Money): Money {
if (this.currency !== other.currency)
throw new Error("Cannot add different currencies")
return new Money(this.amount + other.amount, this.currency)
}
}
// shared-kernel/entities/Product.ts
export class Product {
constructor(
readonly id: string,
readonly name: string,
readonly basePrice: Money,
readonly category: Category
) {}
}
Équipe Catalog l’Utilise
// catalog/src/domain/models/PricedProduct.ts
import { Product, Money } from "shared-kernel"
export class PricedProduct {
constructor(
readonly product: Product,
readonly currentPrice: Money
) {}
applyDiscount(percent: number): Money {
const discount = this.currentPrice.amount * (percent / 100)
return new Money(
this.currentPrice.amount - discount,
this.currentPrice.currency
)
}
}
Équipe Inventory l’Utilise
// inventory/src/domain/models/StockLevel.ts
import { Product } from "shared-kernel"
export class StockLevel {
constructor(
readonly product: Product,
readonly quantity: number,
readonly warehouse: Warehouse
) {}
canFulfill(requiredQty: number): boolean {
return this.quantity >= requiredQty
}
}
Évolution du Shared Kernel
// Les équipes se mettent d'accord: ajouter une taxe
// shared-kernel/value-objects/Money.ts (version 2.0)
export class Money {
constructor(
readonly amount: number,
readonly currency: "USD" | "EUR" | "GBP",
readonly taxRate: number = 0 // nouveau!
) {}
getTaxAmount(): number {
return this.amount * this.taxRate
}
}
// Les deux équipes mettent à jour leurs usages
Gestion des Changements du Kernel
Processus Strict
1. Une équipe propose un changement
2. Les 2 équipes discutent et se mettent d'accord
3. Code review approuvée par représentant de chaque équipe
4. Changement merged
5. Les 2 équipes mettent à jour leurs usages
Versioning
shared-kernel@1.0.0 (current)
├── Team A uses 1.0.0
└── Team B uses 1.0.0
shared-kernel@2.0.0 (breaking change)
├── Team A migrates (delay acceptable)
├── Team B migrates (peut avoir un timeline différent)
└── Possibilité de coexistence si critique
Quand Divergence Menace le Kernel ?
Signes d’alerte 🚨
Catalog Team: "On voudrait ajouter X au Product"
Inventory Team: "Mais nous n'en avons pas besoin, ça pollue"
↓ (Conflit de besoins)
Stratégies de Résolution
- Compromis : Ajouter X, Inventory ignore
- Séparation : Créer ProductCatalog et ProductInventory (plus de Kernel)
- Abstraction : Créer une interface plus générique
Patterns Apparentés
Shared Kernel vs. Conformist
- Shared Kernel : Les deux équipes contrôlent ensemble
- Conformist : Une équipe impose, l’autre accepte
Shared Kernel vs. Partnership
- Shared Kernel : Partagent du code
- Partnership : Partagent juste une interface / protocole
Checklist de Mise en Place ✓
- Clairement identifié ce qui est vraiment partagé
- Les deux équipes veulent vraiment collaborer
- Un responsable principal pour chaque domaine du kernel
- Un processus de validation clair (code review)
- Une stratégie de versioning
- Pas de secret : tout le monde voit le code
Ressources et Lectures
- Domain-Driven Design par Eric Evans - Shared Kernel
- Building Evolutionary Architectures - Shared Libraries
- DDD Crew - Context Mapping
Notes de Facilitation pour l’Atelier
Animation
- Demandez : “Qu’est-ce qui est vraiment partagé ?”
- Testez : “Si on devait diverger, qu’est-ce qui casse ?”
- Documentez : “Qui maintient ça ?”
Pièges Communs
- ⚠️ Kernel qui grandit : finit par contenir du non-partagé
- ⚠️ Pas de processus : changements non coordonnés
- ⚠️ Une équipe domine : pas vraiment du partage
- ⚠️ Pas de version : débats “c’était comment avant ?”
Questions Provocatrices
- “Et si les besoins divergent complètement ?”
- “Qui brise le kernel si un changement cassant est nécessaire ?”
- “Combien de fois par an vous changez le kernel ?”
- “Pourriez-vous survivre si vous dupliquez plutôt que partagez ?”