.okf/architecture.md
Attributes / Messaging.Core
│
▼
Core ◄── Messaging ◄── JobQueues / CommandRules
│
▼
Library (FastEndpoints) ──► Security, OpenApi, OData, AspVersioning, HealthChecks, Agents.*
│
▼
Generator (analyzer) + Generator.Cli (serializer contexts)
| Layer | Role |
|---|---|
FastEndpoints.Attributes | Shared attributes/contracts; multi-TFM including netstandard2.0 for generator |
FastEndpoints.Core | Service resolution (IServiceResolver, ServiceResolverClient shared resolve façade), assembly scanning |
FastEndpoints.Messaging.Core | ICommand / IEvent / handler interfaces |
FastEndpoints.Messaging | In-process command/event bus |
FastEndpoints.JobQueues | Background jobs over commands + storage SPI |
FastEndpoints (Library) | HTTP endpoints, binding, validation, middleware, config |
FastEndpoints.Security | JWT bearer helpers, cookies, refresh/revocation |
FastEndpoints.OpenApi | Microsoft.AspNetCore.OpenApi document pipeline |
FastEndpoints.Generator | Roslyn generators (discovered types, ACL, reflection cache, service registration, generic processors) |
FastEndpoints.Generator.Cli | Build-time JSON serializer context generation |
FastEndpoints.Testing | AppFixture, collection fixtures, WAF cache for integration tests |
| Messaging.Remote* | gRPC RPC for remote command/event execution (MessagePack by default; wire format pluggable) |
Request path (simplified): AddFastEndpoints registers discovery data → UseFastEndpoints/MapFastEndpoints maps routes → FeRequestHandler resolves endpoint instance → bind → validate → pre-processors → (ResponseStarted short-circuit) → OnBeforeHandle → optional SkipHandlerIfResponseStarted short-circuit → HandleAsync/ExecuteAsync → post-processors → send response.
Startup/mapping split (Src/Library/Main/): public facades stay on MainExtensions (AddFastEndpoints / UseFastEndpoints / MapFastEndpoints, plus internal BuildRoute for OpenApi/Agents friend usage). Mapping orchestration is EndpointRouteMapper; auth policy materialization is EndpointSecurityPolicies; accepts/produces API explorer defaults are EndpointProducesMetadata; binder/validator precompile is EndpointWarmup. Request execution remains FeRequestHandler → EndpointBootstrap → Endpoint.ExecAsync.
Discovery ownership: AddFastEndpoints resolves the type list once (EndpointData.DiscoverTypes for reflection, or source-generated DiscoveredTypes). EndpointData builds the HTTP endpoint definition catalog only (Found). Messaging handler registration is owned by MessagingExtensions.RegisterHandlers into CommandHandlerRegistry (same path used by standalone AddMessaging). Skip AddMessaging when AddFastEndpoints already ran; both must not invent a second registration owner.
Attributes, Core, Messaging.Core).Mcp, A2A) reference Library; share internal types via linked Src/Agents/Shared/*.cs (not a separate NuGet).InternalsVisibleTo to FastEndpoints.Mcp / FastEndpoints.A2A (Src/Library/Metadata.cs). Consumed internals are a binary contract across independently versioned packages; stock in gotchas.md.UseFastEndpoints(c => …) (Config / Cfg).MessagingExtensions.RegisterHandlers (from AddMessaging or as a side path of AddFastEndpoints), or DI/test helpers.AddHandlerServer / remote client connection). The wire format is chosen by an
IRpcMarshallerFactory and defaults to MessagePack. AddHandlerServer(marshaller:) sets it server-side;
RemoteConnection.MarshallerFactory sets it per client connection. Both sides also take the bound gRPC method name from
the factory, so they always agree (MessagePack keeps the historical empty name).FastEndpoints.Messaging.Remote.Reflection is an opt-in satellite package holding the protobuf wire
format and gRPC server reflection (AddHandlerReflection / MapHandlerReflection). It generates Google.Protobuf descriptors
from the command CLR types, so protobuf/reflection dependencies stay out of Messaging.Remote.AddJobQueues<TJob, TStorage>(); storage provider is app-supplied. Optional business-key idempotency via JobQueueOptions.IdempotencyKeyFor<TCommand>(Func<TCommand,string?>) + storage record IHasIdempotencyKey + provider uniqueness / DuplicateJobException.IJobStorageProvider / IJobStorageRecord implementations.(QueueID, IdempotencyKey) while the row exists (including completed); not filtered to incomplete-only.FastEndpoints.Security (Src/Security): JWT bearer, cookies, refresh, revocation.Configure(): AllowAnonymous(), roles/permissions/policies; AccessControl(...) can emit constants via Generator. Global options: Config.Security.IFeatureFlag, call FeatureFlag<T>() to disable an endpoint at runtime.AddAuthenticationJwtBearer, AdminOnly, UseJwtRevocation<T>(), UseAntiforgeryFE. Sample JWT keys are test-only.IEndpoint; public base is Endpoint<TRequest[, TResponse]>.AddFastEndpoints(DiscoveredTypes.All) (+ generator).FastEndpoints.snk (public key in Directory.Build.props / InternalsVisibleTo).Directory.Packages.props (ManagePackageVersionsCentrally).Src/Agents/Directory.Build.props imports parent then overrides).Src/Library/Main/MainExtensions.csSrc/Library/Main/EndpointRouteMapper.csSrc/Library/Endpoint/Endpoint.csSrc/Library/Metadata.csSrc/Security/Src/Agents/Directory.Build.props