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 💭

  1. Qu’est-ce qui est vraiment partagé ? (vs. similaire)
  2. Comment gérons-nous les changements du noyau ?
  3. Qui est responsable de maintenir le Shared Kernel ?
  4. Qu’arrive-t-il si les équipes divergent dans leurs besoins ?
  5. 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

  1. Compromis : Ajouter X, Inventory ignore
  2. Séparation : Créer ProductCatalog et ProductInventory (plus de Kernel)
  3. 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 ?”