{"id":51109412,"url":"https://github.com/gaevoy/gaev-modular-arch","last_synced_at":"2026-06-24T16:02:17.620Z","repository":{"id":365854828,"uuid":"1108483083","full_name":"gaevoy/gaev-modular-arch","owner":"gaevoy","description":"Modular architecture demo: feature isolation via contract/impl split and IoC, implemented in both React (TypeScript) and .NET 10 (C#)","archived":false,"fork":false,"pushed_at":"2026-06-19T06:49:45.000Z","size":76,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-19T08:27:08.887Z","etag":null,"topics":["aspnet-core","csharp","dependency-injection","dotnet","inversify","modular-architecture","react","typescript","vite"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gaevoy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-02T14:06:14.000Z","updated_at":"2026-06-19T06:49:48.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/gaevoy/gaev-modular-arch","commit_stats":null,"previous_names":["gaevoy/gaev-modular-arch"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/gaevoy/gaev-modular-arch","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gaevoy%2Fgaev-modular-arch","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gaevoy%2Fgaev-modular-arch/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gaevoy%2Fgaev-modular-arch/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gaevoy%2Fgaev-modular-arch/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gaevoy","download_url":"https://codeload.github.com/gaevoy/gaev-modular-arch/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gaevoy%2Fgaev-modular-arch/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34739426,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-24T02:00:07.484Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aspnet-core","csharp","dependency-injection","dotnet","inversify","modular-architecture","react","typescript","vite"],"created_at":"2026-06-24T16:02:17.128Z","updated_at":"2026-06-24T16:02:17.604Z","avatar_url":"https://github.com/gaevoy.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Modular Architecture Demo\n\nThe same architectural pattern — **[Dependency Inversion Principle](https://en.wikipedia.org/wiki/Dependency_inversion_principle)** (feature isolation via contract/impl split and an IoC container) — implemented in two ecosystems side by side.\n\n| | React | .NET |\n|---|---|---|\n| Language | TypeScript | C# 13 |\n| Runtime | Browser (Vite + ESM) | ASP.NET Core 10 |\n| IoC container | inversify | `Microsoft.Extensions.DependencyInjection` |\n| Module unit | npm workspace package | class library project |\n| Contract | `*-contract` package | `*.Contracts` project |\n| Implementation | `*-impl` package | `*.Impl` project |\n| Registration | `bootstrap.ts` → `registerBundle()` | `Program.cs` → `AddXxxFeature()` |\n\n---\n\n## The Problem\n\nAs a codebase grows, features start importing each other directly:\n\n```\nDashboard feature\n  └── imports UserService     (concrete class)\n  └── imports CurrencyService (concrete class)\n```\n\nThis is convenient at first, but creates tight coupling over time:\n\n- **Testing** Dashboard requires real instances of UserService and CurrencyService — or brittle mocking at the import level.\n- **Changing** a service's internals forces updates in every feature that imported it directly.\n- **Replacing** an implementation (e.g. swapping a mock for a real API) requires touching all its consumers.\n- **Scaling** becomes hard — there is no safe boundary between features, so changes ripple unpredictably.\n\n---\n\n## The Solution\n\nSplit every feature into two modules:\n\n```\nuser-contract   ← interface IUserService + data types. Zero dependencies.\nuser-impl       ← class UserService implements IUserService. Imports the contract only.\n```\n\nWire them together in one place — the host — using an IoC container:\n\n```\nhost (bootstrap / Program.cs)\n  └── registers: IUserService → UserService\n  └── registers: ICurrencyService → CurrencyService\n  └── registers: IDashboardService → DashboardService\n```\n\nNow the Dashboard feature depends on the *contract*, not the *impl*:\n\n```\nDashboard feature\n  └── imports IUserService     (interface — from user-contract)\n  └── imports ICurrencyService (interface — from currency-contract)\n```\n\nThe dashboard module has no reference to `UserService` or `CurrencyService`. You can replace, stub, or lazy-load either without touching Dashboard at all.\n\n---\n\n## Rules\n\nEnforced by the module system (package.json dependencies / .csproj project references) — not by convention or code review. Breaking them is a **build error**.\n\n**Contract modules**\n- No dependencies on any other feature — not contracts, not impls\n- No logic — only interfaces, data types, and IoC symbols\n\n**Impl modules**\n- May depend on their own contract\n- May depend on other features' *contracts* (to call their services)\n- Must never depend on another feature's *impl*\n\n**Host**\n- The only module that imports impl packages\n- Wires the IoC container once at startup\n- Never uses concrete types after that — everything goes through contracts\n\n---\n\n## The Three Features\n\nBoth implementations use the same three features:\n\n- **User** — in-memory store, CRUD operations\n- **Currency** — hard-coded rate table, currency conversion\n- **Dashboard** — depends on User + Currency *contracts* only; the concrete example of cross-feature injection without impl-to-impl coupling\n\n---\n\n## Dependency Graph\n\n```mermaid\nflowchart BT\n    HOST[\"Host\"]\n\n    subgraph user[\"User Feature\"]\n        UC[\"Contract\"]\n        UI[\"Impl\"]\n    end\n\n    subgraph dashboard[\"Dashboard Feature\"]\n        DC[\"Contract\"]\n        DI[\"Impl\"]\n    end\n\n    subgraph currency[\"Currency Feature\"]\n        CC[\"Contract\"]\n        CI[\"Impl\"]\n    end\n\n    UI --\u003e UC\n    CI --\u003e CC\n    DI --\u003e DC\n    DI --\u003e UC\n    DI --\u003e CC\n\n    HOST --\u003e UI\n    HOST --\u003e CI\n    HOST --\u003e DI\n\n    classDef contracts fill:#1a3a5c,stroke:#5b9bd5,color:#cce5ff\n    classDef impl fill:#2a3a1a,stroke:#7daf3d,color:#d5f0b0\n    classDef host fill:#3a1a1a,stroke:#d54b4b,color:#ffd5d5\n\n    class UC,CC,DC contracts\n    class UI,CI,DI impl\n    class HOST host\n\n    style user fill:none,stroke:#888,stroke-dasharray:5 5\n    style currency fill:none,stroke:#888,stroke-dasharray:5 5\n    style dashboard fill:none,stroke:#888,stroke-dasharray:5 5\n```\n\n**Legend:** blue — Contract (interfaces, types \u0026 symbols) \u0026nbsp;·\u0026nbsp; green — Impl (services, components, hooks) \u0026nbsp;·\u0026nbsp; red — Host (wiring)\n\n---\n\n## Repo Layout\n\n```\nreact/      React + Vite + inversify\ndotnet/     ASP.NET Core 10 + Minimal APIs\n```\n\n- [react/README.md](react/README.md)\n- [dotnet/README.md](dotnet/README.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgaevoy%2Fgaev-modular-arch","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgaevoy%2Fgaev-modular-arch","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgaevoy%2Fgaev-modular-arch/lists"}