SAIF.Agency.Api is a conventional three-layer ASP.NET Web API 2 service hosted
in IIS. Controllers bind and authorise the request, a service layer holds the
agency rules, and NHibernate maps the AGENCY schema behind an IUnitOfWork
scoped to the request. Autofac is the composition root and is registered before
routing during Application_Start, so every controller is constructor-injected
and no controller resolves a service itself.
The service is hosted in IIS. Global.asax instantiates AgencyApiApplication :
System.Web.HttpApplication, whose Application_Start runs the App_Start configuration classes, including WebApiConfig.Register(HttpConfiguration).
Aspect
Finding
Host
IIS (System.Web.HttpApplication) — no OWIN, no WCF, no self-host
The service makes no downstream HTTP calls, so no SAIF.Web.API.Clients node
appears in the pipeline: every response is assembled from the AGENCY schema.
All three controllers derive from AgencyApiBase : ApiController rather than
from ApiController directly. The base class resolves the caller's Windows
identity into an AgencyCaller record used for audit stamping, exposes
NotFoundForAgency(agencyNumber) and ValidationFailure(result) helpers that
produce the service's standard ApiErrorDto body, and writes an ILogService
entry carrying the correlation id added by AgencyApiCorrelationHandler. Every
derived controller inherits that behaviour, so error bodies and audit
attribution are identical across the API.
AutofacConfig.Register builds a single ContainerBuilder. It calls
RegisterCommonDependencies("agencyDb", "AGENCY") from SAIF.Web.Mvc.Extensions,
which supplies ILogService, the Environment API configuration reader, and the
NHibernate session factory configured for the named Oracle database and schema;
the FluentNHibernate class maps are picked up by scanning the
SAIF.Agency.Api.Data assembly. It then registers the API's own controllers with
RegisterApiControllers(Assembly.GetExecutingAssembly()), loads
AgencyServiceModule for the service and repository registrations, and finally
builds the container and assigns the resolver.
Registration
Lifetime
Resolved by
Purpose
IAgencyService → AgencyService
InstancePerRequest
AgenciesController
Agency lookup, search, roster, and contact update rules
IAgentService → AgentService
InstancePerRequest
AgentsController, AgencyService
Agent lookup and appointment filtering
IAppointmentService → AppointmentService
InstancePerRequest
AppointmentsController
Appointment reads and creation
IAgencyRepository → AgencyRepository
InstancePerRequest
AgencyService
NHibernate queries and writes against the AGENCY schema
IContactValidator → ContactValidator
InstancePerLifetimeScope
AgencyService
Email and phone validation supplied by SAIF.Framework.Validation
IUnitOfWorkFactory("oracle")
SingleInstance
IUnitOfWork resolution
NHibernate unit of work factory for the Oracle AGENCY schema
Maps AgencyNotFoundException to 404 Not Found, AgencyValidationException to 400 Bad Request, AgencyStateConflictException and StaleAgencyVersionException to 409 Conflict, and anything else to 500 Internal Server Error. Writes an ApiErrorDto carrying code, message, and the correlation id, and logs the full exception through ILogService.
Adds or echoes the X-Correlation-Id header on every request and response so a failure can be traced from a consumer's log to this service's log. Does not translate exceptions.
Catches StaleAgencyVersionException to add the current version to the 409 body before rethrowing for the filter to format.
A caller never receives exception detail. ApiErrorDto carries a stable error
code, a message written for the caller, and the correlation id; the stack trace
and the Oracle error text stay in the ILogService entry.
Attribute routing only: a new endpoint is invisible unless it carries a
[Route] attribute, because no convention route exists to fall back on.
Every request runs inside one IUnitOfWork. A controller action that needs
two independent transactions has no supported way to express that.
Controllers must derive from AgencyApiBase; a controller derived directly
from ApiController would produce a different error body and would not stamp
the correlation id.
The service holds no cache. Every read is an Oracle round trip, which keeps
agency data current at the cost of load on the AGENCY schema.