Skip to content

Application architecture

The Policy Services Portal is an ASP.NET Core 8.0 Razor Pages application on .NET 8, hosted in IIS in-process under the /PolicyPortal base path. Its Startup class derives from the SAIF BolRazorLayoutStartup start-up base class†, so authentication, page chrome, menu and policy context come from the BolMiddleware package† rather than from application source. The application contributes the pages, the page models that derive from the BolMiddleware chain†, a small set of application services, and the Kendo UI front end. It has no database of its own: business data is reached through SAIF API clients and the PolicyCore, Billing and document-rendering APIs behind them.

Layer diagram

flowchart TD
    Browser["Browser: Razor Pages, Kendo UI, page script modules"] --> Pages[Razor Pages and page models]
    Pages --> Base["BolMiddleware page models and layouts"]
    Pages --> Services[Application services]
    Base --> BolApis[BOL Context, Menu, Policy Header and Security APIs]
    Services --> Clients[SAIF API clients]
    Clients --> Apis[Back-end APIs]

Layer descriptions

Layer Projects / packages Responsibility
Presentation SAIF.Policy.Portal (Pages\, wwwroot\) Razor Pages, page models, partial views and page script modules; no business logic
Page chrome and context BolMiddleware† Layouts, view components, web context, menu and policy header resolution, and the BolContextModel to BolPolicyModel page-model chain
Host and authentication SAIF.NetCore.ServiceExtensions† OIDC sign-in through the OidcRazorStartup base of BolRazorLayoutStartup
Application services SAIF.Policy.Portal (Services\) UserPermissionService, which wraps the security service for in-handler permission checks, and AutoMapper profiles for view models
Billing SAIF.Policy.Billing Premium balance operations that the screens call, such as refreshing a balance
Policy data and documents SAIF.Policy.Services†, SAIF.Policy.Documents† Policy summary retrieval and declaration page rendering through the PolicyCore and document-rendering APIs

Host start-up

The application uses a minimal host: Program.cs creates the WebApplication builder and drives a Startup instance by hand rather than calling UseStartup<T>.

# Call Effect Source
1 WebApplication.CreateBuilder(args) Creates the builder and loads appsettings.json, appsettings.<Environment>.json and environment variables Program.cs:5
2 new Startup(builder.Configuration) The constructor sets EnableNewtonsoftJsonSerializer and EnableSessionMiddleware to true; both are switches on BolRazorLayoutStartup† Program.cs:6, Startup.cs:18
3 startup.ConfigureServices(builder.Services) Runs the registrations in rows 4 to 9 Program.cs:7
4 base.ConfigureServices(services) Inherited registrations from BolRazorLayoutStartup†: OIDC authentication, the IBolServicesContainer service set, and the BasicAppMap section bound to IAppMap; called first so the application registrations that follow can replace them Startup.cs:26
5 services.AddRazorPages(...) Adds Razor Pages and the AddPageRoute("/Policy/Search", "") convention that serves Policy Search at the site root Startup.cs:28
6 services.Configure<PortalSettings>(...) Binds the AppSettings section to PortalSettings Startup.cs:34
7 services.AddScoped<...>() (four calls) Registers the application services listed under Composition root Startup.cs:36
8 services.AddAutoMapper(typeof(Startup)) Registers IMapper and every Profile in the assembly Startup.cs:41
9 services.AddMemoryCache() Registers IMemoryCache Startup.cs:42
10 builder.Build() Builds the WebApplication Program.cs:8
11 startup.Configure(app, app.Environment) Runs the middleware registrations listed under Middleware order Program.cs:9
12 app.Run() Starts the host Program.cs:10

Middleware order

Startup.Configure registers three calls of its own and then calls the base class last.

# Middleware Effect Source
1 UsePathBase("/PolicyPortal") Strips the /PolicyPortal base path so every later middleware and the route table see the path without it Startup.cs:48
2 UseDeveloperExceptionPage() in Development, otherwise UseExceptionHandler("/Error") Shows the developer exception page locally and the stock /Error page elsewhere Startup.cs:50
3 UseMiddleware<CorrelationIdMiddleware>() Reads or creates the X-Correlation-Id value, returns it as a response header and adds it to the log scope Startup.cs:59
4 base.Configure(app, env) Inherited pipeline†: session (enabled by the constructor switch), static files, routing, authentication, authorization and Razor Pages endpoints; called last, so rows 1 to 3 run ahead of it Startup.cs:61

Route table

Routes come from the file path under Pages\. The conventions and route templates below change that default. Every URL is relative to the /PolicyPortal base path set at middleware step 1. The application registers no MapControllers or MapGet endpoints, so its AJAX requests are named page handlers, listed on Hosted endpoints. /Error and /AccessDenied have no route template and are served at their file URLs.

# Page URL Defined by Source
1 /Policy/Search / Conventions.AddPageRoute("/Policy/Search", "") Startup.cs:30
2 /Policy/Summary /Policy/Summary/{policyNumber?} @page "{policyNumber?}" Pages/Policy/Summary.cshtml:1
3 /Payroll/Reports /Payroll/Reports/{policyNumber?} @page "{policyNumber?}" Pages/Payroll/Reports.cshtml:1
4 /Endorsement/New /Endorsement/New/{policyNumber?} @page "{policyNumber?}" Pages/Endorsement/New.cshtml:1
5 /Premium/Ledger /Premium/Ledger/{policyNumber?} @page "{policyNumber?}" Pages/Premium/Ledger.cshtml:1

Page model hierarchy

classDiagram
    PageModel <|-- BolContextModel
    BolContextModel <|-- BolMenuModel
    BolMenuModel <|-- BolPolicyModel
    BolMenuModel <|-- SearchModel
    BolPolicyModel <|-- SummaryModel
    BolPolicyModel <|-- ReportsModel
    BolPolicyModel <|-- NewModel
    BolPolicyModel <|-- LedgerModel

BolContextModel, BolMenuModel and BolPolicyModel come from the BolMiddleware package†. They override the page-handler lifecycle hooks, so context resolution, menu and policy header loading, and the redirect to /Policy/Search when a policy page has no policy number in context run before every handler of a derived page†. No application page model overrides those hooks, and the application has no base page model of its own.

Page model Base Roles Serves
SearchModel BolMenuModel† PolicyServices.Read, PolicyServices.Underwriting Policy Search
SummaryModel BolPolicyModel† PolicyServices.Read, PolicyServices.Underwriting; refresh balance is PolicyServices.Underwriting only Policy Summary
ReportsModel BolPolicyModel† PolicyServices.Read, PolicyServices.Underwriting Payroll Reports
NewModel BolPolicyModel† PolicyServices.Underwriting New Endorsement
LedgerModel BolPolicyModel† PolicyServices.Read, PolicyServices.Underwriting Premium Ledger

Layouts, view imports and middleware includes

The four BOL layouts, the three view components and MenuNavigation.js ship in the BolMiddleware package† and are not files in this repository.

Item Value Source
_ViewStart layout _PolicyLayout† Pages/_ViewStart.cshtml:2
Layout override _MenuOnlyLayout† on /Policy/Search, which has no policy context Pages/Policy/Search.cshtml:3
_ViewImports tag helpers Microsoft.AspNetCore.Mvc.TagHelpers, Kendo.Mvc Pages/_ViewImports.cshtml:2
MiddlewareIncludes/siteStyles Adds the Kendo UI theme and the compiled site.css to every page Pages/MiddlewareIncludes/siteStyles.cshtml
MiddlewareIncludes/siteScripts Adds jQuery, the Bootstrap bundle, Kendo UI and site.js to every page Pages/MiddlewareIncludes/siteScripts.cshtml

Composition root

Location: src/SAIF.Policy.Portal/Startup.cs (ConfigureServices)

ConfigureServices calls base.ConfigureServices first†, then adds Razor Pages with its page-route convention, binds PortalSettings, registers the four application services as scoped, and finishes with AutoMapper and the memory cache. The application registers no logging provider of its own. The services supplied by base.ConfigureServices, including IBolServicesContainer†, are not repeated in the table.

Service Implementation Lifetime Configuration used
IPolicySummaryService PolicySummaryService in SAIF.Policy.Services† Scoped AppSettings:PolicyCoreApiUrl
IDeclarationRenderer DeclarationRenderer in SAIF.Policy.Documents† Scoped AppSettings:DocumentApiUrl
IPremiumBalanceService PremiumBalanceService in SAIF.Policy.Billing Scoped AppSettings:BillingApiUrl
IUserPermissionService UserPermissionService Scoped None
IMemoryCache MemoryCache Singleton None

Filters, conventions and tag helpers

Kind Type Scope Effect
Page filter OnPageHandlerExecuting override† BolContextModel and every derived page model Resolves the web context and loads menu and policy header data before the handler runs; redirects to /Policy/Search when a policy page has no policy number
Page route convention AddPageRoute("/Policy/Search", "") /Policy/Search Serves Policy Search at the site root as well as at its file URL
Authorization [SecurityApiAuthorize("PolicyServices.Read,PolicyServices.Underwriting")] BolPolicyModel† and SearchModel Only users holding one of the two roles can open the page
Authorization [SecurityApiAuthorize("PolicyServices.Underwriting")] SummaryModel.OnPostRefreshBalanceAsync and NewModel Only underwriting staff can refresh a balance or start an endorsement; others are redirected to /AccessDenied
Anti-forgery Razor Pages default for unsafe verbs every POST handler Validates the request verification token. No page model declares [IgnoreAntiforgeryToken] and Startup registers no override, so every POST handler, including OnPostRefreshBalanceAsync and OnPostReadAsync, is validated
Tag helper set Kendo.Mvc application Makes the Kendo UI tag helpers, such as kendo-grid and kendo-dropdownlist, available on every page

Client-side stack

Asset Contents Loaded by
wwwroot/lib/kendo-ui/js/kendo.all.min.js Kendo UI 2024.1.130, local copy pinned in libman.json MiddlewareIncludes/siteScripts.cshtml
wwwroot/lib/jquery, wwwroot/lib/bootstrap jQuery 3.7.1 and the Bootstrap 5.3.3 bundle, local copies MiddlewareIncludes/siteScripts.cshtml
wwwroot/css/site.css Compiled from wwwroot/scss/site.scss through gulpfile.js; do not edit the compiled output MiddlewareIncludes/siteStyles.cshtml
Script module Screen
wwwroot/js/policy/search.js Policy Search
wwwroot/js/policy/summary.js Policy Summary
wwwroot/js/payroll/reports.js Payroll Reports

Key patterns

  • Inherited context: policy context, menu and header are resolved by the BolMiddleware page-model chain† and are cited from each screen as inherited behavior, not rewritten per page.
  • Handlers as the endpoint layer: AJAX, grid and file requests are named page handlers on the page that uses them; there are no API controllers.
  • Thin page models: handlers delegate to the application services above and map results to view models with AutoMapper.
  • Server-rendered partials: dialog content is a partial view fetched on demand and injected into a container, not built in script.

Integration points

Integration Type Direction Libraries involved
BOL Context, Menu, Policy Header and Security APIs API outbound BolMiddleware†
PolicyCore API API outbound SAIF.Policy.Services†
Billing API API outbound SAIF.Policy.Billing
Document rendering API API outbound SAIF.Policy.Documents†
OIDC identity provider OIDC outbound SAIF.NetCore.ServiceExtensions†

Design constraints

  • The application is served under the /PolicyPortal base path; script modules build URLs from the page or from ~/, never from a root-relative literal.
  • base.ConfigureServices is called first and base.Configure last, as the package requires†.
  • A policy page needs a policy number in WebContext; the redirect when it is missing belongs to the base page model†.
  • The application has no database access; all business data comes through the APIs above.
  • site.css is compiled output; change site.scss instead.
  • Application index
  • Overview
  • Dependencies
  • Configuration
  • Hosted endpoints
  • Operations and processes
  • Business rules
  • Security and authorization
  • Data model
  • Tech debt

[!NOTE] Statements marked with * are inferred from code patterns. Statements marked with ** could not be verified from available source. Statements marked with † are sourced from package documentation retrieved with the Legends CLI.

Sources

  • src/SAIF.Policy.Portal/Program.cs
  • src/SAIF.Policy.Portal/Startup.cs
  • src/SAIF.Policy.Portal/SAIF.Policy.Portal.csproj
  • src/SAIF.Policy.Portal/Pages/_ViewStart.cshtml
  • src/SAIF.Policy.Portal/Pages/_ViewImports.cshtml
  • src/SAIF.Policy.Portal/Pages/MiddlewareIncludes/siteScripts.cshtml
  • src/SAIF.Policy.Portal/Pages/MiddlewareIncludes/siteStyles.cshtml
  • src/SAIF.Policy.Portal/Pages/Policy/Summary.cshtml.cs
  • src/SAIF.Policy.Portal/gulpfile.js