Skip to content

Commit c55185a

Browse files
committed
feat: add example screens for Application Structure and Enterprise Application, update navigation
1 parent 7303b43 commit c55185a

6 files changed

Lines changed: 1087 additions & 0 deletions

File tree

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import {type JSX} from "react";
2+
import {Shell} from "../../../src/components/shell/shell.js";
3+
import {AppStructureScreen} from "../../../src/screens/examples/app-structure-screen.js";
4+
5+
const Page = (): JSX.Element => (
6+
<Shell>
7+
<AppStructureScreen />
8+
</Shell>
9+
);
10+
11+
export default Page;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import {type JSX} from "react";
2+
import {Shell} from "../../../src/components/shell/shell.js";
3+
import {EnterpriseExampleScreen} from "../../../src/screens/examples/enterprise-example-screen.js";
4+
5+
const Page = (): JSX.Element => (
6+
<Shell>
7+
<EnterpriseExampleScreen />
8+
</Shell>
9+
);
10+
11+
export default Page;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import {redirect} from "next/navigation";
2+
3+
const Page = (): never => {
4+
redirect("/examples/app-structure");
5+
};
6+
7+
export default Page;

packages/reca-docs/src/navigation.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,13 @@ export const navigation: INavGroup[] = [
6262
{label: "Configuration", path: "/api/configuration"}
6363
]
6464
},
65+
{
66+
label: "Examples",
67+
items: [
68+
{label: "Application Structure", path: "/examples/app-structure"},
69+
{label: "Enterprise Application", path: "/examples/enterprise-app"}
70+
]
71+
},
6572
{
6673
label: "Advanced",
6774
items: [
Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
"use client";
2+
3+
import {type JSX} from "react";
4+
import {Alert} from "@mui/material";
5+
import {DocContent} from "../../components/doc-content/doc-content.js";
6+
7+
export const AppStructureScreen = (): JSX.Element => (
8+
<DocContent>
9+
<h1>Application Structure</h1>
10+
<p>
11+
A well-structured ReCA application follows a <strong>layered architecture</strong>{" "}
12+
inspired by Clean Architecture and Feature-Sliced Design. The goal is simple:
13+
business logic lives in <code>src/</code>, completely independent of the framework.
14+
Next.js (or any other framework) is used only for routing.
15+
</p>
16+
17+
<h2>Top-Level Layout</h2>
18+
<pre><code>{`my-app/
19+
├── app/ ← Next.js routing only (no business logic here)
20+
├── src/ ← all application code (framework-agnostic)
21+
│ ├── components/
22+
│ ├── screens/
23+
│ ├── services/
24+
│ ├── repositories/
25+
│ ├── models/
26+
│ ├── hooks/
27+
│ ├── helpers/
28+
│ ├── utils/
29+
│ ├── validations/
30+
│ ├── localization/
31+
│ ├── styles/
32+
│ └── icons/
33+
├── public/
34+
├── tests/
35+
└── package.json`}</code></pre>
36+
37+
<h2>app/ — Routing Only</h2>
38+
<p>
39+
The <code>app/</code> directory belongs entirely to Next.js App Router. Every file
40+
here is a thin routing shell: it imports a page component from <code>src/pages/</code>
41+
and renders it inside the layout. <strong>No business logic, no stores, no
42+
services here.</strong>
43+
</p>
44+
<pre><code>{`// app/products/page.tsx ← just a route entry point
45+
"use client";
46+
47+
import type { JSX } from "react";
48+
import { Shell } from "../../src/components/shell/Shell";
49+
import { Layout } from "../../src/components/layout/Layout";
50+
import { ProductListPage } from "../../src/screens/product/list/ProductListPage";
51+
52+
const ProductsPage = (): JSX.Element => (
53+
<main>
54+
<Shell>
55+
<Layout>
56+
<ProductListPage />
57+
</Layout>
58+
</Shell>
59+
</main>
60+
);
61+
62+
export default ProductsPage;`}</code></pre>
63+
64+
<Alert severity="info" sx={{my: 2}}>
65+
Because all real code lives in <code>src/</code>, migrating from Next.js to Remix,
66+
Vite, or any other framework means changing only the <code>app/</code> folder.
67+
Everything in <code>src/</code> stays untouched.
68+
</Alert>
69+
70+
<h2>src/ — All Application Code</h2>
71+
<p>
72+
The <code>src/</code> folder is structured by <strong>concern</strong>, not by feature.
73+
Each sub-folder has a single, well-defined responsibility.
74+
</p>
75+
76+
<h3>pages/ (or screens/)</h3>
77+
<p>
78+
Full-screen page components. Each page corresponds to a route and composes
79+
multiple smaller components. Pages own their own store, styles, and tests —
80+
exactly like regular components (see <strong>components/</strong> below).
81+
</p>
82+
<Alert severity="warning" sx={{my: 2}}>
83+
In Next.js the name <code>pages/</code> is reserved for the Pages Router.
84+
If you use the App Router, name this folder <code>screens/</code> to avoid
85+
conflicts — the purpose and structure are identical.
86+
</Alert>
87+
<pre><code>{`src/screens/
88+
├── product/
89+
│ ├── list/
90+
│ │ ├── ProductListPage.tsx
91+
│ │ ├── ProductListPage.store.ts
92+
│ │ ├── ProductListPage.styles.ts
93+
│ │ ├── ProductListPage.unit.spec.ts
94+
│ │ └── components/ ← page-local sub-components
95+
│ └── card/
96+
│ └── ...
97+
└── order/
98+
└── list/
99+
└── ...`}</code></pre>
100+
101+
<h3>components/</h3>
102+
<p>
103+
Reusable UI components shared across multiple pages. Every component is a
104+
<strong> self-contained folder</strong> with five files:
105+
</p>
106+
<pre><code>{`src/components/header/
107+
├── Header.tsx ← React component (view only)
108+
├── Header.store.ts ← ReCA store with all state and logic
109+
├── Header.styles.tsx ← styled-components / CSS-in-JS styles
110+
├── Header.spec.ts ← unit tests (Jest / Mocha)
111+
└── Header.md ← component documentation`}</code></pre>
112+
<p>
113+
This co-location rule means you always know exactly where to look. Renaming or
114+
deleting a component is safe — all its moving parts are in one place.
115+
</p>
116+
<pre><code>{`// Header.tsx — thin view, delegates everything to the store
117+
import { useStore } from "reca";
118+
import { HeaderStore } from "./Header.store";
119+
import { HeaderRoot } from "./Header.styles";
120+
121+
export const Header = () => {
122+
const store = useStore(HeaderStore);
123+
124+
return (
125+
<HeaderRoot>
126+
<span>{store.userName}</span>
127+
<button onClick={() => store.signOut()}>Sign out</button>
128+
</HeaderRoot>
129+
);
130+
};`}</code></pre>
131+
132+
<h3>services/</h3>
133+
<p>
134+
Services contain business logic. They are split into <strong>four sub-folders</strong>
135+
that mirror the classic Clean Architecture layers:
136+
</p>
137+
<pre><code>{`src/services/
138+
├── domain-services/ ← pure business rules, no side-effects
139+
├── app-services/ ← orchestration: coordinate domain services + repositories
140+
├── infr-services/ ← infrastructure: analytics, logging, monitoring
141+
└── view-services/ ← UI-level services: toasts, modals, navigation`}</code></pre>
142+
<ul>
143+
<li>
144+
<strong>domain-services/</strong> — encapsulate core business rules.
145+
They know nothing about HTTP, the DOM, or React. Example:{" "}
146+
<code>PricingService</code> calculates discounts based on customer tier.
147+
</li>
148+
<li>
149+
<strong>app-services/</strong> — application use-cases. They orchestrate
150+
domain services and repositories to fulfil a user action. Example:{" "}
151+
<code>OrderService</code> fetches orders, applies filters, and formats
152+
them for display.
153+
</li>
154+
<li>
155+
<strong>infr-services/</strong> — infrastructure concerns: error tracking,
156+
analytics events, OpenTelemetry spans. They are injected like any other
157+
service but wrap third-party SDKs.
158+
</li>
159+
<li>
160+
<strong>view-services/</strong> — services that talk to the UI layer:{" "}
161+
<code>ToastService</code> shows notifications,{" "}
162+
<code>NotificationService</code> manages the notification centre.
163+
They are singletons injected into stores that need to trigger UI feedback.
164+
</li>
165+
</ul>
166+
167+
<Alert severity="info" sx={{my: 2}}>
168+
The dependency rule flows in one direction:{" "}
169+
<code>view-services</code><code>app-services</code><code>domain-services</code>.
170+
Infrastructure services can be injected at any layer. Repositories are only
171+
called from <code>app-services</code>.
172+
</Alert>
173+
174+
<h3>repositories/</h3>
175+
<p>
176+
Data-access layer. Each repository is responsible for one API resource and
177+
wraps all HTTP calls for that resource. Stores and services never call{" "}
178+
<code>fetch</code> directly — they always go through a repository.
179+
</p>
180+
<pre><code>{`src/repositories/
181+
├── ProductRepository.ts ← GET /products, POST /products, etc.
182+
├── OrderRepository.ts
183+
├── UserRepository.ts
184+
└── base/
185+
└── BaseRepository.ts ← shared fetch logic, auth headers, error handling`}</code></pre>
186+
187+
<h3>models/</h3>
188+
<p>
189+
TypeScript interfaces and types that describe the domain data. Models are plain
190+
data shapes — no logic, no dependencies:
191+
</p>
192+
<pre><code>{`src/models/
193+
├── Product.ts ← interface IProduct { id: string; title: string; ... }
194+
├── Order.ts
195+
└── User.ts`}</code></pre>
196+
197+
<h3>hooks/</h3>
198+
<p>
199+
Custom React hooks that encapsulate UI logic which doesn&apos;t belong to a store.
200+
Typical examples: <code>useDebounce</code>, <code>useMediaQuery</code>,{" "}
201+
<code>useOutsideClick</code>.
202+
</p>
203+
204+
<h3>helpers/</h3>
205+
<p>
206+
Domain-aware pure functions with no side-effects: format a price, build a
207+
breadcrumb path, map an API response to a view model. Helpers know about your
208+
domain but have no external dependencies.
209+
</p>
210+
211+
<h3>utils/</h3>
212+
<p>
213+
Generic utilities with no domain knowledge: deep clone, chunk an array,
214+
debounce a function. These could be extracted into a separate package without
215+
any changes.
216+
</p>
217+
218+
<h3>validations/</h3>
219+
<p>
220+
Form validation schemas (Zod, Yup, or hand-written). Kept here so they can
221+
be reused across pages and tested in isolation.
222+
</p>
223+
224+
<h3>localization/</h3>
225+
<p>
226+
Translation files and the i18n configuration. Components import keys, never
227+
raw strings.
228+
</p>
229+
230+
<h3>styles/</h3>
231+
<p>
232+
Global CSS variables, theme tokens, and shared styled-components. Import
233+
from here instead of duplicating values across component files.
234+
</p>
235+
236+
<h3>icons/</h3>
237+
<p>
238+
SVG icon components. Centralising them here avoids scattered inline SVGs and
239+
makes it easy to swap the icon set.
240+
</p>
241+
242+
<h2>Why Framework Independence Matters</h2>
243+
<p>
244+
Because <code>app/</code> is only routing glue, the entire <code>src/</code>{" "}
245+
folder can be moved to a different framework without touching a single store
246+
or service. For example, switching from Next.js App Router to Pages Router
247+
means rewriting only the files in <code>app/</code>:
248+
</p>
249+
<pre><code>{`// Before: app/products/page.tsx (App Router)
250+
import { ProductListPage } from "../../src/screens/product/list/ProductListPage";
251+
export default function Page() { return <ProductListPage />; }
252+
253+
// After: pages/products.tsx (Pages Router)
254+
import { ProductListPage } from "../src/screens/product/list/ProductListPage";
255+
export default function Page() { return <ProductListPage />; }
256+
257+
// src/ is completely unchanged`}</code></pre>
258+
259+
<h2>Summary</h2>
260+
<ul>
261+
<li><code>app/</code> — Next.js routing only; thin wrappers that delegate to <code>src/</code></li>
262+
<li><code>src/screens/</code> — full-screen page components with co-located store, styles, and tests (use <code>screens/</code> instead of <code>pages/</code> to avoid the Next.js naming conflict)</li>
263+
<li><code>src/components/</code> — reusable components; each is a self-contained folder with view, store, styles, tests, and docs</li>
264+
<li><code>src/services/</code> — business logic in four layers: domain, app, infrastructure, view</li>
265+
<li><code>src/repositories/</code> — all HTTP/data-access code; never called directly from components</li>
266+
<li><code>src/models/</code> — pure TypeScript interfaces; no logic</li>
267+
<li><code>src/hooks/</code>, <code>helpers/</code>, <code>utils/</code>, <code>validations/</code>, <code>localization/</code> — supporting utilities, each with a clear scope</li>
268+
</ul>
269+
</DocContent>
270+
);

0 commit comments

Comments
 (0)