Dossier · Internal docs
Internal · Rebuild Estimates

Rebuild Estimates — .NET + Angular

Estimate for rewriting Dossier (server, client, root, portal) on a .NET + Angular stack, using the existing TypeScript codebase as a reference but not as a port. The receiving team owns the knowledge by re-implementing.

Source codebase, for reference

Package Purpose Files LOC
core shared types, DB schemas, expression engine 45 6,306
server Fastify API, ~45 routes, PDF, auth 61 7,968
client tenant React app (port 4103) 113 18,145
root admin React app (port 4104) 59 12,573
portal client portal + intake widget (port 4105) 41 5,391
Total 319 ~50,400

Plus 7 SQL migrations, 7 Drizzle schema files, ~10 test files in core/server.

Target stack assumed

  • Backend: .NET 8/9, ASP.NET Core minimal APIs (or controllers), EF Core on PostgreSQL 17, FluentValidation, JWT auth
  • PDF: iText 7 or QuestPDF + PdfSharpCore (AcroForm read/fill, merge)
  • Frontend: Angular 17+ (standalone components, signals), Angular Material or PrimeNG, ng-pdf-viewer or equivalent for PDF preview
  • Testing: xUnit + Testcontainers (Postgres) for backend; Jest + Cypress/Playwright for frontend
  • Observability: OpenTelemetry .NET SDK, Aspire dashboard (already used)

Assumptions

  • 1 dev day = 8 productive hours. Estimates are mid-range; ranges given for risk.
  • Team has solid .NET + Angular experience but is new to bankruptcy/court forms — domain learning is included.
  • The existing TS source, schemas, forms, and domains/ artifacts are available as reference. Schemas/forms JSON are reused as-is (not rewritten).
  • Existing PostgreSQL schema is kept; EF Core models map to existing tables. No data migration needed for greenfield rebuild.
  • The website-public and website-private packages (static HTML) are out of scope — same files serve unchanged.
  • Marketing, design polish, accessibility audit, and security review are budgeted lightly; assume the existing UX is the spec.

Summary

Area Backend Frontend Tests Total (days)
1. Foundation & scaffolding 8 4 3 15
2. Auth & multi-tenancy 5 3 3 11
3. Expression engine + schema runtime 18 9 27
4. Form/binding engine + resolver/validator 12 6 18
5. PDF engine (extract, fill, merge, export) 13 4 17
6. Server API — domain CRUD (~45 routes) 28 8 36
7. Client app (tenant UI) 58 10 68
8. Root app (admin UI) 36 5 41
9. Portal app (intake + widget) 18 3 21
10. Cross-cutting (telemetry, seed, docs) 8 3 2 13
Subtotal 92 122 53 267
Domain learning / integration buffer (~15%) 40
Grand total ~307 dev days

That's roughly 15–16 person-months. With a typical squad of 1 backend lead + 1 backend + 2 frontend = 4 devs, expect ~4 calendar months of full-time work, plus a small QA tail.

Range under realistic risk: 270 (best case) — 360 (worst case) dev days.


1. Foundation & scaffolding — 15 days

Task Days
.NET solution layout (API, Core, Infrastructure, Shared.Contracts), CI pipeline (GitHub Actions or Azure DevOps), Dockerfile 3
EF Core context, conventions, migrations baseline mirroring 7 existing SQL files 5
Angular workspace with 3 apps (client, root, portal) + shared libs (shared-ui, shared-data, shared-engine-types) 3
Common HTTP interceptors (auth, error, retry), shared DTOs, OpenAPI client generation (NSwag or Refitter) 2
Local dev orchestration: docker-compose (Postgres + Aspire), seed scripts, README 2

Risks: Multi-app Angular workspace boilerplate sometimes drags. NSwag/OpenAPI codegen friction with .NET minimal APIs.

2. Auth & multi-tenancy — 11 days

Task BE FE Tests
JWT access + refresh, password hashing (BCrypt), /auth/sign-in, sign-up, sign-out, refresh, me 4 2
Tenant scoping middleware (every query filtered by tenantId), require-root policy 1 1
Angular AuthService, route guards, token refresh interceptor across all 3 apps 3

3. Expression engine + schema runtime — 27 days (the IP)

This is the densest, most subtle code in the codebase. Reference: packages/core/src/expressions/* (parser, evaluator, 22 Excel-style functions, type coercion) and packages/core/src/schema/* (flatten, validate).

Task BE Tests
Tokenizer + parser for expression DSL ($child.field, $.field, array [] selectors) 5 2
Evaluator + type coercion + null semantics 4 2
22 built-in functions (logic, math, text, date, comparison) 3 2
Schema model (entries, groups, namespaces, imports), flatten + validate 4 2
Schema registry, defaults, dependency loading 2 1

Risks: Subtle behavior in the existing parser (operator precedence, error tolerance, array semantics) is easy to get wrong. The receiving team must port the test suite faithfully or write a contract-equivalence test against the TS engine.

4. Form/binding engine + resolver/validator — 18 days

Task BE Tests
Form composition (recursive children with keys/aliases), bindings, conditions 4 2
Binding resolver (key → form fields), source-of-truth merge across entries 3 1
Condition checker (gates fields/forms based on expressions) 2 1
Validator (severity-graded validations) and readiness scoring 2 1
Cycle detection across schema/form references 1 1

5. PDF engine — 17 days

Task BE Tests
AcroForm field extraction (key, type, page, rect, label) — replaces pdf-fields.ts 4 1
PDF filler (fill by key, type-aware: text, checkbox, radio, dropdown) — replaces pdf-filler.ts using iText/PdfSharp 5 2
Merged-PDF export and ZIP-of-PDFs export, font handling, value formatting 3 1
Preview rendering pipeline (server-side fill + client-side viewer) 1

Risks: iText AGPL licensing forces commercial license or migration to QuestPDF/PdfSharpCore — confirm before starting. AcroField filling parity with pdf-lib is the highest single technical risk: appearance streams, field flattening, and checkbox/radio export values need careful parity tests against the TS output.

6. Server API (~45 routes) — 36 days

Existing routes total ~4,400 LOC. Grouped:

Route group BE Tests
Schemas (CRUD, import, clone, entry validation, dependants) 4 1
Forms (CRUD, tree, child composition, field-map, usage) 5 1
Cases base + entries, filings, contacts, tasks, notes, events, billing, attachments, history, readiness, validations, intake-invites, forms, activity 9 2
Top-level: contacts, events, billing, data-sources, marketplace, files (preview/export) 4 1
Root admin: tenants, schemas, forms (incl. documents.ts 1,359 LOC — the biggest single file), data-sources 5 2
Portal/intake routes + intake service + rate-limit 3 1

Risks: routes/root/documents.ts is unusually large (1,359 LOC) and likely contains form-management orchestration that maps poorly onto vanilla CRUD. Plan a deeper read before sizing.

7. Client app (tenant UI) — 68 days

18,145 LOC, ~30 pages/components. The Data tab and component library dominate.

Area FE Tests
Layout shell: sidebar (collapsible), top bar, theme, command palette, new-case dialog 6 1
Dashboard (stats, quick actions, activity feed, deadlines) 3
Cases list + filters 2
Case detail shell + 11-tab transforming sidebar + nested routes 3 1
Data tab (split-pane: datasheet form left, live PDF preview right, validation panel, drift, pending edits, sections nav, multiple views) 12 2
Filings tab + new-filing modal + detail rail 5 1
Documents (checklist + uploads) 2
Tasks, Notes 3
Billing tab (fees + payments + time) 3
Case calendar 2
History 1
E-Filing pre-flight 2
Client portal tab (manage shareable link/permissions) 2
Forms library (per-case + tenant-wide) 3
Settings (8 sub-pages) 4
Help (30 articles, search, sections filter) 2
Contacts directory (filters, roles) 2
Calendar firm-wide (week/month, upcoming sidebar) 4 1
Billing firm-wide (receivable/collected/overdue/outstanding) 2
UI primitives library (~25 components: badge, button, card, dialog, dropdown, data-grid, master-detail, page-wrapper, view-toggle, dossier badges/rings/chips/popovers) 5
Cypress/Playwright e2e — golden paths (login, create case, fill form, file) 5

Risks: Migrating ~25 shadcn-style React components to Angular Material/PrimeNG never lands one-to-one — expect re-design on at least 5 of them. The dossier/* design system pieces (provenance popover, ring meter, schema-key chip, status dot) are domain-specific and worth keeping visually faithful — that demands custom Angular components, not Material substitutes.

8. Root app (admin UI) — 41 days

12,573 LOC, ~30 pages, dominated by form-editing tooling.

Area FE Tests
Layout shell, auth, breadcrumbs, master-detail layout 3
Dashboard + stats 2
Schemas: list + 7 detail pages (overview, raw, entries, imports, dependants, ui-config, layout) 7 1
Forms: list + 13 detail pages including bindings, field-map, fields, parents, children, schema, stats, test runner, tree visualizer, upload + PDF field overlay, validations 14 2
Data sources: list + mappings + overview 3
Tenants page 2
Specialist components: expression-input, pdf-field-overlay, graph-canvas, tag-input, dropzone, data-import, results-grid 5 1
Cypress smoke flows 1

Risks: pdf-field-overlay (drag-to-rect over PDF page) and graph-canvas (binding/dependency graph) are non-trivial and have no off-the-shelf Angular equivalent — budget protected here, may still slip.

9. Portal app (intake + widget) — 21 days

5,391 LOC. Five intake modes (wizard, grid, spread, chat, voice), embeddable widget, branded shell.

Area FE Tests
Branded shell, theme resolution, intake landing 3
Wizard mode (multi-step + review) — primary mode 4 1
Grid mode 2
Spread mode (spreadsheet-like) 2
Chat mode (engine + conversational UI) 3 1
Voice mode 2
Embeddable widget (bubble + entry + panel; cross-origin loader) 3 1
Login, dashboard, complete pages 2
Intake session hook, fallback config/schema/token 1

Risks: Voice mode depends on browser SpeechRecognition — Angular wrapper needs revisiting. Embeddable widget (<script> tag drop-in on a tenant's site) has packaging implications: build as a single self-contained bundle, not as part of the Angular workspace's normal build.

10. Cross-cutting — 13 days

Task BE FE Tests
OpenTelemetry .NET wiring (Fastify-equivalent auto-instrumentation), Aspire export 3
Structured logging via Serilog, correlation IDs 1
Seed scripts: 2 schemas + 69 leaf forms + 19 composite forms + 4 intake configs ingested into the new DB 3 1
Auto-history logging on case mutations (existing TS pattern) ported to EF Core interceptors 1 1
Theme sync (light/dark) across all 3 Angular apps + portal preview 2
Internal docs (deployment, migration runbook, API quickstart) 1
Bug-fix / integration buffer

What's intentionally excluded

  • Marketing site (website-public, website-private) — static HTML, reusable as-is.
  • Form processing pipeline (domain_tools/ scripts, prompts) — not a customer-facing app; can stay in TS or be ported later.
  • Domain content — schemas, form JSONs, data-source recipes are reused, not rewritten.
  • Production hardening beyond parity: load testing, hardening WAF rules, SOC2 controls, formal pen test, accessibility audit. Add ~20–30 days if any of these are in scope.
  • New features: this estimate is parity-only. Anything from docs/todo/todo.md (e-filing integration, PACER sync, payment processing, etc.) is on top.

Risk factors (in order of impact)

  1. PDF AcroForm parity with pdf-lib — appearance streams, checkbox export values, font fallbacks. Mitigation: byte-level diff harness against existing engine on day 1.
  2. Expression engine semantics — port the entire test suite from packages/core/src/expressions/__tests__/ and run the new engine against it as a contract test before writing any callers.
  3. PDF library licensing — iText AGPL forces a commercial license or alternative. Decide before sprint 1.
  4. routes/root/documents.ts (1,359 LOC) — single largest server file, likely orchestration-heavy. Read before sizing this sprint.
  5. Component-library mismatch — Angular Material aesthetic differs from the Chambers/cream design language. Budget for custom components on the dossier-design pieces or accept visual drift.
  6. Domain learning curve — bankruptcy court forms, schedules, means test, exemptions. Pair the backend lead with the original team for the first 2 weeks; the 15% buffer assumes this happens.

How to refine this estimate

Spend 2 dev days reading these specific files before locking the number — they account for most of the remaining uncertainty:

  • packages/core/src/expressions/parser.ts + evaluator.ts (engine subtlety)
  • packages/server/src/lib/pdf-filler.ts + pdf-fields.ts (PDF parity)
  • packages/server/src/routes/root/documents.ts (1,359 LOC outlier)
  • packages/client/src/pages/case-tabs/data/* (the most complex tab)
  • packages/root/src/pages/form-* (13 form-admin pages)
Source: docs/estimates.md