You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

6.5 KiB

Implementation Plan: FMV Portfolio View with Plaid Account Linking & Asset Drill-Down

Branch: 009-fmv-plaid-drilldown | Date: 2026-03-22 | Spec: spec.md Input: Feature specification from /specs/009-fmv-plaid-drilldown/spec.md

Summary

Restore 7 admin-gated portfolio features (Overview, Holdings, Summary, Markets, Watchlist, FIRE, X-Ray) for all authenticated users by moving them out of the accessAdminControl nav gate. Add an FMV Dashboard page aggregating account values (reusing existing getAccountsWithAggregations() API). Wire account cards to the existing account-detail-dialog for asset drill-down. Integrate Plaid for automated brokerage account linking and ongoing investment sync via a new PlaidItem model, NestJS Plaid module, @plaid/link-initialize Angular client, and a BullMQ sync queue.

Technical Context

Language/Version: TypeScript 5.x (strict mode)
Primary Dependencies: Angular 21+ (standalone components, signals), NestJS 11+ (module-based DI), Prisma ORM, plaid v41+ (Node SDK), @plaid/link-initialize (client), @nestjs/bull (BullMQ)
Storage: PostgreSQL (Docker port 5434→5432), Redis (Docker port 6379→6379)
Testing: Jest (unit + integration)
Target Platform: Web application (server: Node.js, client: SPA)
Project Type: Nx monorepo web-service + SPA (apps: api, client; libs: common, ui)
Performance Goals: FMV Dashboard < 3s load, drill-down < 2 clicks, Plaid Link < 2 min, 50 holdings load < 5s
Constraints: Plaid rate limit 100 req/min (production), access tokens encrypted at rest (AES-256-GCM)
Scale/Scope: Single-tenant family office, ~5 accounts, ~50 holdings, 1 user

Constitution Check

GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.

Principle Status Notes
I. Nx Monorepo Structure PASS All changes within existing api, client, common projects. No new Nx projects.
II. NestJS Module Pattern PASS New PlaidModule follows module/controller/service pattern. Queue follows existing DataGatheringModule pattern.
III. Prisma Data Layer PASS New PlaidItem model + Account extension via Prisma migration. No direct SQL.
IV. TypeScript Strict PASS All code under strict mode, path aliases used.
V. Simplicity First PASS US1 is nav-only (no API changes). US2/US3 reuse existing API endpoints. US4/US5 add new module only where needed. Max 3 projects touched: api, client, common.
VI. Interface-First Design PASS Contracts defined in contracts/ before implementation. Shared types in @ghostfolio/common.
Max 3 Nx projects per feature PASS api (Plaid module, migration), client (FMV page, nav changes), common (shared interfaces). No ui lib changes needed.

Post-Phase 1 Re-Check: PASS — No violations introduced during design. PlaidItem model is minimal. Existing APIs reused for FMV/drill-down. One new NestJS module for Plaid.

Project Structure

Documentation (this feature)

specs/009-fmv-plaid-drilldown/
├── plan.md              # This file
├── research.md          # Phase 0 output — 8 research decisions
├── data-model.md        # Phase 1 output — PlaidItem model, Account extensions
├── quickstart.md        # Phase 1 output — setup & verification steps
├── contracts/           # Phase 1 output — API contracts
│   ├── plaid-api.md     # 7 Plaid endpoints
│   └── navigation-fmv.md # Nav restructure + existing endpoint usage
└── tasks.md             # Phase 2 output (NOT created by /speckit.plan)

Source Code (repository root)

apps/api/src/
├── app/
│   ├── plaid/                          # NEW — NestJS Plaid module
│   │   ├── plaid.module.ts             # Module registering providers, imports
│   │   ├── plaid.controller.ts         # HTTP endpoints (link-token, exchange, sync, webhook)
│   │   ├── plaid.service.ts            # Business logic (Plaid API calls, token encryption)
│   │   └── interfaces/                 # Request/response DTOs
│   │       ├── create-link-token.interface.ts
│   │       └── exchange-token.interface.ts
│   └── ...existing modules unchanged
├── services/
│   ├── queues/
│   │   └── plaid-sync/                 # NEW — BullMQ queue for Plaid sync
│   │       ├── plaid-sync.module.ts
│   │       ├── plaid-sync.processor.ts # @Processor — handles SYNC_INVESTMENTS jobs
│   │       └── plaid-sync.service.ts   # Queue service — addJobToQueue
│   └── ...existing services unchanged

apps/client/src/
├── app/
│   ├── components/
│   │   └── header/
│   │       ├── header.component.html   # MODIFIED — nav restructure (move legacy items)
│   │       └── header.component.ts     # MODIFIED — new menu arrays
│   ├── pages/
│   │   └── fmv/                        # NEW — FMV Dashboard page
│   │       ├── fmv-page.component.ts   # Standalone component
│   │       ├── fmv-page.component.html # Hero total + account cards
│   │       └── fmv-page.routes.ts      # Route definitions
│   └── services/
│       └── plaid-link.service.ts       # NEW — Angular service wrapping @plaid/link-initialize

libs/common/src/lib/
├── interfaces/
│   └── plaid-item.interface.ts         # NEW — shared PlaidItem type
├── config.ts                           # MODIFIED — PLAID_SYNC_QUEUE constant
└── permissions.ts                      # UNCHANGED (no permission changes needed)

prisma/
├── schema.prisma                       # MODIFIED — PlaidItem model, Account extensions
└── migrations/
    └── YYYYMMDD_add_plaid_item/        # NEW — migration for PlaidItem + Account fields

Structure Decision: Follows existing Nx monorepo conventions. Changes span 3 projects (api, client, common) which is within the 3-project maximum. New plaid/ module in API mirrors existing module structure (e.g., account/, portfolio/). New plaid-sync/ queue mirrors existing data-gathering/ queue. New fmv/ page follows existing page pattern (e.g., accounts/, home/).

Complexity Tracking

No violations — all gates pass. Feature uses 3 Nx projects (maximum) and follows established patterns.

Violation Why Needed Simpler Alternative Rejected Because
(none)