AI lär sig av mönster du redan skapat — inte av mönster du tänkt skapa. Grunden måste kodas manuellt, deliberat och dokumenteras så att AI kan följa den.
AI-modeller är tränade på existerande kod och mönster. De är utmärkta på att reproducera och varitera kända mönster, men de uppfinner inte nya arkitekturer som passar ditt specifika systems kontext. Om du låter AI bestämma arkitekturen från noll riskerar du:
Grunden är de strukturella beslut och basklasser som definierar hur resten av systemet ska byggas. I ett typiskt C# DDD-projekt inkluderar det:
| Komponent | Vad det definierar | Varför det måste vara manuellt |
|---|---|---|
| AggregateRoot<TId> | Basklass för alla aggregat, domänhändelsehantering | Alla aggregat ärver detta — ett fel sprider sig överallt |
| Result<T> / Error | Felhanteringsmonad för domänoperationer | Konsistent felhantering kräver ett genomtänkt kontrakt |
| ValueObject-basklass | Equality, validering, immutabilitet | Record-semantiken måste passa domänens krav |
| IRepository<T> | Kontraktet för alla repositories | Generics, async-mönster och felhantering måste vara genomtänkta |
| Projektstruktur / lager | Var kod hör hemma och beroendesriktning | Felaktig lagerstruktur sprider beroenden som är omöjliga att ta bort |
| Namnkonventioner | Naming för kommandon, events, tjänster etc. | AI genererar inkonsistent om konventionen inte är explicit |
En genomtänkt AggregateRoot-basklass som AI sedan kan referera till:
namespace MyApp.Domain.Common;
/// <summary>
/// Basklass för alla DDD Aggregate Roots.
/// Hanterar domänhändelser och identitet.
/// </summary>
public abstract class AggregateRoot<TId> where TId : notnull
{
private readonly List<IDomainEvent> _domainEvents = [];
public TId Id { get; protected init; } = default!;
public IReadOnlyList<IDomainEvent> DomainEvents => _domainEvents.AsReadOnly();
protected void AddDomainEvent(IDomainEvent domainEvent)
=> _domainEvents.Add(domainEvent);
public void ClearDomainEvents()
=> _domainEvents.Clear();
}
/// <summary>
/// Markörinterface för alla domänhändelser.
/// Implementeras som records för immutabilitet.
/// </summary>
public interface IDomainEvent { }
/// <summary>
/// Railway-oriented felhantering för domänoperationer.
/// Kasta aldrig exceptions från domänmetoder — returnera Result.
/// </summary>
public sealed class Result<T>
{
public T? Value { get; }
public DomainError? Error { get; }
public bool IsSuccess => Error is null;
private Result(T value) => Value = value;
private Result(DomainError error) => Error = error;
public static Result<T> Success(T value) => new(value);
public static Result<T> Failure(DomainError error) => new(error);
public Result<TOut> Map<TOut>(Func<T, TOut> mapper) =>
IsSuccess ? Result<TOut>.Success(mapper(Value!)) : Result<TOut>.Failure(Error!);
}
public sealed record DomainError(string Code, string Message);
Result<T> används för alla felfall — inga undantag i domänenDomainError — immutabelt och jämförbartArchitecture Decision Records (ADR) är korta dokument som fångar ett arkitekturellt beslut: vad som beslutades, varför och vilka alternativ övervägdes. De är källmaterialet för dina instruktionsfiler.
# Mappstruktur för ADR:er
docs/
└── decisions/
├── ADR-001-aggregate-root-pattern.md
├── ADR-002-result-type-error-handling.md
├── ADR-003-repository-interface.md
└── ADR-004-event-sourcing-vs-crud.md
# ADR-002-result-type-error-handling.md
# ADR-002: Result<T> för felhantering i domänlagret
## Status
Accepterad
## Kontext
Domänmetoder kan misslyckas av affärsmässiga skäl (t.ex. ogiltig email-adress,
otillräckligt saldo). Vi behöver ett konsistent sätt att kommunicera dessa fel
till application-lagret utan att använda exceptions.
## Beslut
Alla public domänmetoder returnerar Result<T> vid fel. Exceptions kastas
aldrig från domänlagret. DomainError innehåller en string-kod och ett meddelande.
## Alternativ som övervägdes
- Exceptions (DomainException) — avvisat pga. exceptions är dyra och flödesstyrning via exceptions är svår att följa
- OneOf<T, Error> — avvisat pga. extern dependency och mer komplex syntax
- Tuples (T value, string? error) — avvisat pga. saknad semantik och svag typning
## Konsekvenser
- Application-lagret måste alltid hantera Result — kan inte ignorera fel
- Testning förenklas — inga exceptions att fånga i tester
- Lärningskurva för nya teammedlemmar som inte känner mönstret
copilot-instructions.md skapad med minst sex regler.instructions.md-fil med applyToNär grunden är kodad kan du be Copilot extrahera reglerna automatiskt:
# Prompt för att generera instruktionsfil från referensimplementation:
Du är en senior C#-arkitekt. Analysera filerna:
#file:src/Domain/Common/AggregateRoot.cs
#file:src/Domain/Common/Result.cs
#file:src/Domain/Orders/Order.cs
#file:src/Domain/Orders/OrderItem.cs
#file:tests/Domain/OrderTests.cs
Generera en .instructions.md-fil med applyTo: "src/Domain/**/*.cs".
Filen ska extrahera EXAKTA regler från koden — inte generella DDD-regler.
Täck: strukturmönster, namnkonventioner, felhanteringskontrakt, händelsehantering,
Value Object-mönster och testmönster som framgår av filerna.
Skriv reglerna som imperativa meningar ("Returnera alltid Result<T>...").
Max 40 regler. Gruppera under tydliga rubriker.
Lös övningarna självständigt. Det finns inget facit — lärandet sker i processen.
← Föregående: Copilot Skills Nästa: AI-genererade Instruktionsfiler →