본문으로 건너뛰기

아키텍처 설계

samples/ai-model-governance/
├── AiModelGovernance.slnx # 솔루션 파일 (8 프로젝트)
├── Directory.Build.props # FunctoriumSrcRoot + 공통 설정
├── Directory.Build.targets # root 상속 차단
├── domain/ # 도메인 레이어 문서 (4개)
├── application/ # 애플리케이션 레이어 문서 (4개)
├── adapter/ # 어댑터 레이어 문서 (4개)
├── observability/ # 관측성 문서 (4개)
├── Src/
│ ├── AiGovernance.Domain/ # Domain Layer
│ │ ├── SharedModels/Services/ # Domain Services (2종)
│ │ └── AggregateRoots/
│ │ ├── Models/ # AIModel + VOs(4) + Specs(2)
│ │ ├── Deployments/ # ModelDeployment + VOs(4) + Specs(3)
│ │ ├── Assessments/ # ComplianceAssessment + Entity(1) + VOs(3) + Specs(3)
│ │ └── Incidents/ # ModelIncident + VOs(4) + Specs(4)
│ ├── AiGovernance.Application/ # Application Layer
│ │ └── Usecases/
│ │ ├── Models/ # Commands(2), Queries(2), Ports(2)
│ │ ├── Deployments/ # Commands(4), Queries(2), Ports(4)
│ │ ├── Assessments/ # Commands(1), Queries(1), EventHandlers(1)
│ │ └── Incidents/ # Commands(1), Queries(2), Ports(1), EventHandlers(1)
│ ├── AiGovernance.Adapters.Infrastructure/ # Mediator, OpenTelemetry, External Services
│ │ ├── ExternalServices/ # IO 고급 기능 (4종)
│ │ └── Registrations/
│ ├── AiGovernance.Adapters.Persistence/ # InMemory, EfCore Repository/Query
│ │ ├── Models/ # Repository(2) + Query(2)
│ │ ├── Deployments/ # Repository(2) + Query(2)
│ │ ├── Assessments/ # Repository(2)
│ │ ├── Incidents/ # Repository(2) + Query(1)
│ │ └── Registrations/
│ ├── AiGovernance.Adapters.Presentation/ # FastEndpoints
│ │ ├── Endpoints/ # HTTP API (15종)
│ │ └── Registrations/
│ └── AiGovernance/ # Host (Program.cs)
└── Tests/
├── AiGovernance.Tests.Unit/ # 단위 테스트
└── AiGovernance.Tests.Integration/ # 통합 테스트

AiModelGovernance.slnx에 8개 프로젝트가 포함됩니다.

<Solution>
<Project Path="Src/AiGovernance.Domain/AiGovernance.Domain.csproj" />
<Project Path="Src/AiGovernance.Application/AiGovernance.Application.csproj" />
<Project Path="Src/AiGovernance.Adapters.Infrastructure/AiGovernance.Adapters.Infrastructure.csproj" />
<Project Path="Src/AiGovernance.Adapters.Persistence/AiGovernance.Adapters.Persistence.csproj" />
<Project Path="Src/AiGovernance.Adapters.Presentation/AiGovernance.Adapters.Presentation.csproj" />
<Project Path="Src/AiGovernance/AiGovernance.csproj" />
<Project Path="Tests/AiGovernance.Tests.Unit/AiGovernance.Tests.Unit.csproj" />
<Project Path="Tests/AiGovernance.Tests.Integration/AiGovernance.Tests.Integration.csproj" />
</Solution>
프로젝트레이어역할
AiGovernance.DomainDomainAggregate, VO, Specification, Domain Service, Domain Event
AiGovernance.ApplicationApplicationCommand/Query Usecase, Port 인터페이스, Event Handler
AiGovernance.Adapters.InfrastructureAdapterMediator, OpenTelemetry, Pipeline, External Service
AiGovernance.Adapters.PersistenceAdapterRepository/Query 구현 (InMemory, EfCore)
AiGovernance.Adapters.PresentationAdapterFastEndpoints HTTP API
AiGovernanceHostProgram.cs, DI 조립, appsettings.json
AiGovernance.Tests.UnitTest단위 테스트 (VO, Aggregate, Domain Service, Architecture)
AiGovernance.Tests.IntegrationTest통합 테스트 (HTTP Endpoint E2E)

AiGovernance (Host)
├── Adapters.Infrastructure → Functorium + Functorium.Adapters + Application
├── Adapters.Persistence → Functorium.Adapters + Application + SourceGenerators
├── Adapters.Presentation → Application
└── Application → Domain
Domain → Functorium + SourceGenerators

핵심 원칙: 의존성은 안쪽으로만 향한다. Domain은 외부에 대해 아무것도 모르고, Application은 포트 인터페이스만 알며, Adapter가 구현한다.


차원표현 수단예시
Aggregate (무엇)1차 폴더Models/, Deployments/, Assessments/, Incidents/
CQRS Role (읽기/쓰기)2차 폴더Repositories/, Queries/, Commands/, EventHandlers/
Technology (어떻게)클래스 접미사EfCore, InMemory, Dapper

파일명 패턴: {Subject}{Role}{Variant}

섹션 제목: “파일명 패턴: {Subject}{Role}{Variant}”
파일 유형패턴예시
Repository{Aggregate}Repository{Variant}.csAIModelRepositoryInMemory.cs, AIModelRepositoryEfCore.cs
Query{Aggregate}Query{Variant}.csAIModelQueryInMemory.cs, DeploymentDetailQueryInMemory.cs
DB 모델{Aggregate}.Model.csAIModel.Model.cs, Deployment.Model.cs
EF 설정{Aggregate}.Configuration.csAIModel.Configuration.cs
UnitOfWorkUnitOfWork{Variant}.csUnitOfWorkInMemory.cs, UnitOfWorkEfCore.cs

3개 Registration 클래스가 각 Adapter의 서비스를 독립적으로 등록합니다.

Registration 클래스등록 항목
AdapterPresentationRegistrationFastEndpoints
AdapterPersistenceRegistrationRepository, Query, UnitOfWork (Observable 래퍼)
AdapterInfrastructureRegistrationMediator, FluentValidation, OpenTelemetry, Pipeline, Domain Service, External Service
// Host Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services
.RegisterAdapterPresentation()
.RegisterAdapterPersistence(builder.Configuration)
.RegisterAdapterInfrastructure(builder.Configuration);
var app = builder.Build();
app.UseAdapterPresentation();
app.Run();
public partial class Program { } // Integration Test 지원

appsettings.jsonPersistence:Provider 값으로 InMemory/Sqlite를 전환합니다.

{
"Persistence": {
"Provider": "InMemory",
"ConnectionString": "Data Source=ai-governance.db"
}
}

AdapterPersistenceRegistration에서 Provider 값에 따라 분기합니다:

switch (options.Provider)
{
case "Sqlite":
services.AddDbContext<GovernanceDbContext>(opt =>
opt.UseSqlite(options.ConnectionString));
RegisterSqliteRepositories(services);
break;
case "InMemory":
default:
RegisterInMemoryRepositories(services);
break;
}

Observable 래퍼를 통해 DI에 등록하므로, Provider를 교체해도 관측성은 자동으로 유지됩니다:

// InMemory
services.RegisterScopedObservablePort<IAIModelRepository, AIModelRepositoryInMemoryObservable>();
// Sqlite
services.RegisterScopedObservablePort<IAIModelRepository, AIModelRepositoryEfCoreObservable>();

OpenTelemetry 3-Pillar 관측성을 RegisterOpenTelemetry + ConfigurePipelines로 설정합니다.

services
.RegisterOpenTelemetry(configuration, AssemblyReference.Assembly)
.ConfigurePipelines(pipelines => pipelines
.UseObservability() // CtxEnricher, Metrics, Tracing, Logging 일괄 활성화
.UseValidation()
.UseException())
.Build();

UseObservability()는 관측성 4종(CtxEnricher, Metrics, Tracing, Logging)을 일괄 활성화합니다. 나머지 파이프라인은 명시적 opt-in으로 등록합니다:

순서미들웨어역할
1UseObservability()CtxEnricher + Metrics + Tracing + Logging 일괄 활성화
2UseValidation()FluentValidation 기반 요청 검증
3UseException()예외 -> DomainError/AdapterError 변환
{
"OpenTelemetry": {
"ServiceName": "AiGovernance",
"ServiceNamespace": "AiGovernance",
"CollectorEndpoint": "http://localhost:18889",
"CollectorProtocol": "Grpc",
"SamplingRate": 1.0,
"EnablePrometheusExporter": false
}
}

외부 서비스 통합에서 사용하는 4가지 LanguageExt IO 패턴입니다.

패턴구현 클래스용도핵심 메서드
Timeout + CatchModelHealthCheckService헬스 체크 타임아웃 처리IO.Timeout(10s) -> .Catch(TimedOut, fallback) -> .Catch(Exceptional, error)
Retry + ScheduleModelMonitoringService지수 백오프 재시도IO.Retry(exponential(100ms) | jitter(0.3) | recurs(3) | maxDelay(5s))
Fork + awaitAllParallelComplianceCheckService5개 기준 병렬 체크forks.Map(io => io.Fork()) -> awaitAll(forks)
BracketModelRegistryService세션 리소스 수명 관리acquire.Bracket(Use: ..., Fin: ...)

모든 외부 서비스는 [GenerateObservablePort]로 관측성이 자동 추가되고, IO<A> -> FinT<IO, A> 변환으로 Application Layer의 FinT LINQ 체인에 합성됩니다.


Terminal window
# 빌드
dotnet build Docs.Site/src/content/docs/samples/ai-model-governance/AiModelGovernance.slnx
# 테스트 (268개)
dotnet test --solution Docs.Site/src/content/docs/samples/ai-model-governance/AiModelGovernance.slnx