Architecture Overview

The Supplier Client is a feature-module Angular application. A thin eagerly loaded shell boots the store, the translation layer and all third-party SDKs; everything a supplier actually looks at is a lazy-loaded module hanging off that shell.

The three layers

Layer Location Responsibility

Core

src/app/core/

Singletons. Auth, HTTP interceptors, NgRX root config, business services, domain enums and models, guards, marketplace connectors. Imported once, by AppModule.

Views

src/app/views/

Everything rendered. Lazy feature pages, the onboarding flow, shared widgets and modals, and the authenticated app shell (theme/).

Vendors

src/app/vendors/

Thin in-repo wrappers around third-party SDKs (Plaid, CloudWatch) that expose an Angular-idiomatic module with a forRoot() config.

CoreModule is a singleton and must never be imported by a feature module. Feature modules pull in what they need from PartialsModule, MaterialModule or the specific shared module that owns the component.

Module graph

AppModule (eager, bootstraps AppComponent)
│
├── CoreModule ................ interceptors, pipes, directives, icon registry
├── FeaturesModule ............ registers every NgRX feature + entity slice
├── PartialsModule ............ shared widgets, modals, components
├── ThemeModule ............... BaseComponent shell, header, aside, footer
├── AuthModule.forRoot() ...... auth store slice + persistent meta-reducer
├── OnboardingModule .......... onboarding shell (eager, routed lazily)
│
├── StoreModule.forRoot() ..... router reducer + metaReducers (storeFreeze in dev)
├── EffectsModule.forRoot([]) . root effects registry
├── StoreRouterConnectingModule ... router state → store (stateKey: 'router')
├── StoreDevtoolsModule ....... logOnly instrumentation
│
├── PybApiModule.forRoot() .... Payability API client (base URLs from environment)
├── MarqetaModule.forRoot() ... Seller Card issuance SDK
├── PlaidModule.forRoot() ..... bank linking SDK
├── SegmentModule.forRoot() ... analytics (loadOnInitialization only in prod)
├── CloudWatchModule.forRoot() ... custom metrics endpoint
├── PageLoadTimeTrackerModule.forRoot(['/dashboard']) ... page timing
├── ServiceWorkerModule.register() ... PWA, enabled in production only
├── NgxPermissionsModule.forRoot()
└── TranslateModule.forRoot() ... local loader, default language `en`
│
└── AppRoutingModule
    └── BaseComponent [AuthGuard]     ← the authenticated shell
        ├── DashboardModule ............... lazy, [DocuSignGuard]
        ├── ReportsModule ................. lazy
        ├── ManageAccountModule ........... lazy, [TwoFactorAuthenticationGuard]
        ├── SellerCardModule .............. lazy
        ├── NewCardModule ................. lazy
        ├── MyMarketplacesPageModule ...... lazy
        ├── OnboardingModule .............. lazy
        ├── ProductOfferingsModule ........ lazy, [DocuSignGuard]
        ├── VendorPayIncomingReportsModule  lazy
        ├── SendMoneyModule ............... lazy
        ├── PartnersModule ................ lazy
        ├── MoreFromPayabilityModule ...... lazy (referral)
        ├── PayshieldSubscriptionModule ... lazy
        ├── BlogPageModule ................ lazy
        ├── SalesCallSummaryModule ........ lazy
        └── DailySummaryModule ............ lazy, [NgxPermissionsGuard]

See Route Tree for the URL each module serves.

src/app/core in detail

Folder Contents

_base/crud/

BaseModel, QueryParamsModel, QueryResultsModel, HttpUtilsService, TypesUtilsService. Generic paging/sorting helpers used by data services.

_base/layout/

Layout config service, subheader service, splash-screen service, translation service, plus the shared pipe set (safe, join, getObject, firstLetter, addPercent, maskedBankAccount, replaceSentence) and directives (contentAnimate, sticky, pybDisabled).

_base/tasks-flow/

The TasksFlow / TaskFlowBuilder mini-engine that drives multi-step modal wizards. See Flows & Tasks Engine.

_config/

MenuConfig (the whole navigation tree, permission-annotated), LayoutConfig, PageConfig, aggrid.config.ts, and URLS_ROUTER — the single source of truth for route strings.

api-connectors/

Marketplace API health monitoring for Amazon and Shopify. See Marketplace API Connectors.

auth/

The complete authentication subsystem: actions, effects, reducers, selectors, guards, interceptors, models, and AuthService. See Authentication.

initializers/

APP_INITIALIZER providers. See Bootstrap.

marqeta/

Marqeta SDK wrapper module for Seller Card operations.

material/

MaterialModule — the curated set of Angular Material modules the app uses.

pyb/

All Payability business logic. Subdivided below.

reducers/

Root AppState type, reducers map (router only — everything else registers via forFeature), and metaReducers (storeFreeze outside production).

reports/

Report infrastructure shared by every report page: AG Grid config, DI tokens, daily-summary and fee-report helpers.

src/app/core/pyb in detail

Folder Contents

animations/

Reusable Angular animation triggers.

components/

AG Grid cell renderers, pagination, reCAPTCHA wrapper, dynamic container.

configs/

Icon registry, media registry, hyperlink service config.

constants/

Contact details, country codes, help-article IDs, LTC flow constants, banner factories.

datasources/

AG Grid server-side datasource adapters.

dictionaries/

Static lookup maps used by forms.

entities/

NgRX Entity collections — countries, marketplaces, supplier marketplaces, onboarding, daily summary report.

enums/

Every domain enum. See Enum Reference.

features/

NgRX feature slices. See Feature Slices.

fields/

Dynamic form field definitions and factories.

flows/

Composed multi-step business flows (advance line, LTC, re-sign contract).

guards/

DocuSignGuard, HelpCenterRedirectGuard, TwoFactorAuthenticationGuard, TwoFactorInfoLoadedGuard.

interfaces/

Cross-cutting TypeScript interfaces.

models/

Client-side domain models and DTO mappers.

pipes/

filterBy pipe.

providers/

Shared DI providers.

services/

~40 domain services. See Service Catalog.

tasks/

Individual FlowTask implementations composed by flows/.

utils/

Subscriber base class, JWT decode, date helpers, error helpers.

validators/

Sync and async reactive-form validators.

Cross-cutting patterns

Smart / presentational split

Container components select from the store and dispatch actions; presentational components receive @Input() and emit @Output(). Presentational components do not inject Store.

OnPush everywhere

ChangeDetectionStrategy.OnPush is the default. Components that mutate their own view model must either produce new references or call markForCheck() explicitly.

The Subscriber base class

src/app/core/pyb/utils/subscriber.class.ts exposes an ngUnsubscribe$ subject and completes it in ngOnDestroy. Any class holding long-lived subscriptions extends it and pipes through takeUntil(this.ngUnsubscribe$). Services do this too — DocusignVerificationService extends Subscriber.

Services are root-provided

Domain services are declared @Injectable({ providedIn: 'root' }). Feature modules only list a provider when the service is genuinely feature-scoped (for example ManageAccountService, ReportsService, ProductOfferingsService).

Configuration by injection token

Grid column sets, banner definitions and connector configuration are supplied as DI tokens rather than hardcoded in components. ReportsModule alone provides FEE_REPORT_COLUMNS_CONFIG_TOKEN, OUTSTANDING_BALANCE_COLUMNS_CONFIG_TOKEN, VENDOR_PAY_INCOMING_REPORT_COLUMNS_CONFIG_TOKEN and several more. This makes the same grid component reusable across reports.

Routes come from URLS_ROUTER

src/app/core/_config/urls-route.const.ts is a frozen (as const) object holding every route string. Navigation code, the menu config and redirect logic all read from it, so a URL change is a single-line edit.

Hash routing

RouterModule.forRoot(routes, { useHash: true }). All URLs carry a , which is also why the DocuSign return URL is built by splitting on in DocusignVerificationService.generateRedirectUrl().

Global side-effect wiring

GlobalSubscriptionsService.init() is the single place where session-scoped background behaviour is started. It is idempotent (guarded by an initialized flag) and it:

  1. initializes the marketplace API connectors,

  2. runs the Plaid bank-verification check,

  3. mirrors calculated permissions into NgxPermissionsService,

  4. subscribes to the welcome-message, Airwallex p-card terms and identity-document-expired dashboard notifications and opens their modals,

  5. evaluates whether the supplier must re-sign their contract,

  6. initializes the account-cancelled, 2FA-setup, suspension-reason and cancellation-status banners,

  7. dispatches fetchFees().