Write your business logic once. Run it in React, Vue, or Angular—or the next framework. CAF (Clean Architecture Frontend) is a library that gives you a framework-agnostic core so the same domain and use cases can run in any frontend by swapping adapters.
The problem
Frontend apps often mix UI, state, and business rules. When you switch frameworks or need to share logic across React, Vue, and Angular, you usually rewrite a lot of code. Clean Architecture helps, but doing it well with a single, framework-agnostic core is hard. CAF provides that core: primitives and interfaces so your domain and use cases stay framework-free.
What is CAF?
CAF is a framework-agnostic core for building frontends with Clean Architecture. You implement domain and application layers once; React, Vue, or Angular (and future frameworks) plug in via small adapter packages. No lock-in to a single UI stack.
In short:
- Framework-agnostic — One core; many UIs.
- Clean Architecture — Clear domain, application, and infrastructure layers.
- Reactive primitives — A single reactive engine (Pulse) and Ploc for presentation logic.
- Pluggable adapters — Routing, HTTP, and UI are interfaces; you (or the ecosystem) implement them.
- TypeScript-first — Typed UseCases, RequestResult, and Plocs end-to-end.
Core ideas
| Concept | What it does |
|---|---|
| Pulse | A single reactive value (like a ref). Use it for one piece of state (e.g. loading flag, current user). |
| Ploc | Presentation Logic Component — a stateful bloc built on Pulse. Holds UI-related state and methods. |
| UseCase | An application operation (command or query). Returns RequestResult<T> (loading, data, error) so the UI can show loading/error/success. |
| RouteRepository | Abstraction over routing. Your app implements it (e.g. with React Router or Vue Router); RouteManager uses it for auth and navigation. |
Domain and application code depend only on these abstractions. Infrastructure (HTTP, routing, storage) and the UI layer depend on your framework; the core does not.
Example: React + TypeScript
You put your domain, use cases, and Plocs in a caf/ folder (framework-agnostic), then wire them at the app root and use React hooks in components.
1. Wire the app with CAFProvider (e.g. in main.tsx or App.tsx):
import { CAFProvider } from '@c-a-f/infrastructure-react';
import { setupUserPloc } from './caf/setup';
const userPloc = setupUserPloc();
export default function App() {
return (
<CAFProvider plocs={{ user: userPloc }}>
<YourApp />
</CAFProvider>
);
}
2. In a component, read state from the Ploc and call methods:
import { usePlocFromContext, usePloc } from '@c-a-f/infrastructure-react';
import type { UserPloc } from '../caf/application';
export function UserList() {
const ploc = usePlocFromContext<UserPloc>('user');
if (!ploc) return null;
const [state] = usePloc(ploc);
return (
<div>
{state.loading && <p>Loading...</p>}
{state.error && <p>Error: {state.error}</p>}
<button onClick={() => ploc.loadUsers()} disabled={state.loading}>
Refresh
</button>
<ul>
{state.users.map((u) => (
<li key={u.id}>{u.name} – {u.email}</li>
))}
</ul>
</div>
);
}
The same UserPloc and use cases can drive a Vue or Angular UI; only the hooks (or composables/injectors) change.
The repo at a glance
The CAF repository is a monorepo with:
-
Core:
@c-a-f/core— UseCase, Ploc, Pulse, ApiRequest, RouteManager, and shared interfaces. -
Infrastructure (adapters):
-
@c-a-f/infrastructure-react— React hooks (usePloc,useUseCase,CAFProvider,useRouteManager,useRouteRepository). -
@c-a-f/infrastructure-vue— Vue composables and providers. -
@c-a-f/infrastructure-angular— Angular services and injectors.
-
-
Optional modules:
@c-a-f/validation(Zod, Yup, etc.),@c-a-f/workflow,@c-a-f/permission,@c-a-f/i18n,@c-a-f/testing,@c-a-f/devtools,@c-a-f/clifor scaffolding.
Examples live in the same repo: example-react, example-vue, example-angular, plus example-vue-graphql and example-angular-websocket. Each app has its own caf/ folder (domain, application, infrastructure) so you can copy the structure into your project.
Who is it for?
- Teams that want one domain/application layer and the option to ship with React, Vue, or Angular.
- Developers who care about testability and clear boundaries (domain vs application vs infrastructure).
- Anyone tired of framework-specific "Clean Architecture" recipes and wants a small, shared core and conventions.
Quick start
# Core + your framework
npm install @c-a-f/core
npm install @c-a-f/infrastructure-react # or -vue, -angular
# Optional: scaffold a project
npm install -g @c-a-f/cli
caf-init
Then create a caf/ folder with domain/, application/, infrastructure/, and a setup that wires repositories, use cases, and Plocs. The docs and README in the repo walk through the exact layout and a minimal flow (e.g. GetUsers UseCase → Ploc → React/Vue/Angular UI).
Where to go next
- GitHub: github.com/ialiaslani/caf
- npm: npmjs.com/org/c-a-f
- Docs: docs-caf.vercel.app (intro, packages, getting started, best practices, ADRs)
If you're looking for a small, type-safe core to share Clean Architecture across React, Vue, and Angular, CAF is worth a look.
Top comments (2)
This is a very strong architectural approach—especially for large-scale and enterprise-grade applications.
In big projects, the real cost isn’t just writing features; it’s maintaining and evolving business rules over time. Having a truly framework-agnostic core where domain and use cases live independently from React, Vue, or Angular significantly reduces long-term technical debt. The ability to swap UI layers without rewriting core logic is a serious strategic advantage.
I also see strong potential in cross-team collaboration. Backend-oriented engineers can contribute directly to the domain and application layers without needing deep knowledge of a specific frontend framework. That creates clearer boundaries, better testability, and stronger alignment with Clean Architecture principles.
For enterprise environments—where multiple frontend clients, long product lifecycles, and framework migrations are common—this kind of separation isn’t just elegant, it’s practical.
CAF definitely looks like it has substantial potential in that space.
Thank you—this is a very thoughtful and accurate assessment.
You’ve touched on the core motivation behind CAF: optimizing for long-term change, not short-term feature velocity. As you said, in enterprise-scale systems the real challenge is evolving business rules safely over years, often across multiple teams and UI clients. Keeping the domain and application layers completely framework-agnostic is what makes that evolution sustainable.
The collaboration aspect you mentioned is especially important. By clearly separating domain and use cases from UI concerns, CAF allows backend-leaning engineers to contribute meaningful value without needing to master a specific frontend framework. That shared ownership leads to better modeling, stronger tests, and fewer architectural regressions over time.
And you’re absolutely right—when framework migrations, parallel frontends, or long product lifecycles are the norm, this level of separation stops being “theoretical cleanliness” and becomes a practical necessity.
Really appreciate you calling that out—this is exactly the problem space CAF is designed to serve.