Real Estate Template is built around a feature-first architecture: rather than grouping every component in one folder and every service in another, each business domain owns its entire vertical slice. TheDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/Ozcaar/real-estate-template/llms.txt
Use this file to discover all available pages before exploring further.
features/properties/ directory holds the property types, Zod schemas, service logic, static data, Pinia store, composables, and all presentational components that deal with properties — and nothing else touches them without going through the service. This makes it straightforward to extend, replace, or even extract a feature into a package without hunting across the whole codebase.
Layer responsibilities
The table below shows the responsibility of each layer. Pages are intentionally thin — they load data, set SEO metadata, and assemble feature components. All business logic travels downward through services and composables, never sideways through shared mutable state.| Layer | Location | Responsibility |
|---|---|---|
| Pages | pages/ | Data loading, SEO metadata, layout composition — no business logic |
| Services | features/*/services/ | Business logic, filtering, sorting, data transformation |
| Stores | features/*/stores/, stores/ | Shared reactive state (Pinia) — feature stores stay local |
| Composables | features/*/composables/, core/composables/ | Reusable reactive logic wrapping services or Vue state |
| Components | features/*/components/, components/{ui,layout,shared}/ | Display only — typed props in, events out, no direct data fetching |
| Schemas | features/*/schemas/ | Zod runtime validation at the data-ingestion boundary |
| Types | features/*/types/, types/ | TypeScript domain types — the single source of truth for shapes |
| Data | features/*/data/ | Static MVP data source — swapped for API calls in future phases |
Project folder map
Global vs feature-owned code
Understanding what belongs where is the most important architectural rule. Use this mental model:Inside features/
Code that understands a specific business domain.
PropertyCard.vue knows what a Property is; propertiesService knows how to filter and sort them. Neither can live in components/ui/ or core/ because they carry domain knowledge.Outside features/
Code with zero domain awareness.
BaseButton.vue has no idea what a property is. formatCurrency() just formats numbers. usePageSeo() just builds meta tags. These are safe to import from anywhere.Global component folders
Three global component directories are registered withpathPrefix: false in nuxt.config.ts, meaning they auto-import by filename — no path segment needed:
<BaseButton> resolves to components/ui/BaseButton.vue, <AppHeader> resolves to components/layout/AppHeader.vue, and <PropertyCard> resolves to features/properties/components/PropertyCard.vue — all without path prefixes in templates.
Feature
*/components/** folders are also scanned so feature components are globally available in templates. This lets pages use <PropertyGrid> without a manual import, while the file remains owned by its feature.Auto-import rules for composables
In addition to components,nuxt.config.ts extends Nuxt’s auto-import scanning to cover the core layer and all feature composables and stores:
useProperties() (inside features/properties/composables/) are available without an explicit import in .vue files. However, page-level SEO helpers — usePageSeo and useJsonLd — are kept out of the auto-import scope by design (they live in core/composables/ but pages import them explicitly to keep SEO logic auditable at the call site).
Architecture layer stack
Architectural rules
These rules are enforced by convention and by code review. Violating them tends to create the coupling problems feature-first architecture is designed to prevent.No feature-specific code in global folders
No feature-specific code in global folders
components/ui/, components/layout/, components/shared/, core/, and utils/ must remain business-agnostic. A BaseCard does not know what a Property is. An AppHeader does not hard-code property navigation. Any code that references a feature’s domain type belongs inside that feature’s directory.No API calls or data fetching inside presentational components
No API calls or data fetching inside presentational components
Components receive data through typed props. They emit events. They never call
$fetch, a service method, or a Pinia action directly. Data fetching is the page’s job; business logic is the service’s job. Keeping components data-free makes them trivially reusable and testable.Business logic lives in services and composables — not in pages
Business logic lives in services and composables — not in pages
Pages should read like a wiring diagram: call the service, pass the result to a component, set SEO. Complex filtering, sorting, scoring, and transformation belong in
features/*/services/ or features/*/composables/. This keeps pages readable and makes the logic reusable across multiple routes.No hardcoded visible text — use i18n keys
No hardcoded visible text — use i18n keys
Every user-visible string in a component must be an
$t('some.key') call. Raw English strings in templates are not acceptable. This applies equally to validation error messages, empty-state labels, and aria attributes. Exceptions: data that originates from an agency’s content files (property titles, descriptions) is already locale-managed by the agency.Validation at the data boundary — not in components
Validation at the data boundary — not in components
Zod schemas in
features/*/schemas/ validate data when it enters the application (e.g. when the static data file is loaded, or when an API response is parsed). Components can assume their props are already valid; they should not contain if (!property.title) guards that mask upstream problems.