Repository navigation
Programming rules (Frontend)
These rules are mandatory for all code created, modified, or refactored in the project.
Before writing new code, developers must review the related existing files and follow the same conventions for structure, naming, typing, and organization.
- Single Responsibility Principle (SRP): Each file, class, or function must have one clear purpose. Responsibilities from different layers must not be mixed.
- Reusability (DRY): Reuse existing Interfaces, DTOs, Services, Components, and Utils instead of creating duplicate implementations.
- Clean Code: Remove dead code, unused variables, and unnecessary imports.
-
Comments: Do not comment obvious code. Prefer descriptive names. In large Templates, use comments only to separate important structural sections such as
<!-- Header -->or<!-- Filters -->. - Consistency: Files of the same type must follow the same structure, spacing, typing, and naming conventions.
The application is divided into the following layers:
-
views/: Complete application screens, such asHomeView.vue. Views may manage local screen state, organize Components, and call Services. They must not implement CRUD operations or access Stores directly. -
components/: Reusable UI elements. Components receive data through Props and communicate actions through Emits. They must not access Stores directly, perform CRUD operations, or contain domain logic. -
services/: Responsible for CRUD operations and domain logic. Services are the only layer allowed to access Stores directly. They must not contain visual code. -
stores/: Contain global or shared application state. Each Store must focus on one domain. Usereffor reactive state andcomputedfor derived state. -
utils/: Contain reusable, stateless technical logic, such as date or price formatting. Utils must not contain domain logic, access Stores, or depend directly on Views or Components. -
dtos/: Contain the data required by a specific operation. -
interfaces/: Define domain contracts and data structures. -
router/: Handles routes, navigation, authentication checks, roles, and permissions. It must not contain CRUD or domain logic. -
HTTP / Axios: The shared HTTP configuration handles technical concerns such as
baseURL, headers, tokens, timeouts, interceptors, and general HTTP errors. Domain-specific API operations belong in Services.
View / Component -> Service -> Store
Router -> AuthService -> AuthStore
Direct Store access from the following layers is not allowed:
View -> Store
Component -> Store
Router -> Store
For internal navigation, use:
RouterLink
router.push()
Do not use <a href=""> for navigation between internal application pages.
Use clear, specific, and descriptive names.
Avoid generic names such as:
data
temp
thing
stuff
other
when a more descriptive name can be used.
Use PascalCase and end the name with View.
HomeView.vue
RestaurantsView.vue
AdminDashboardView.vue
Use PascalCase.
StarRating.vue
StatusBadge.vue
BaseModal.vue
Use names that identify the domain.
AuthService
RestaurantService
ReservationService
ReviewService
The Service name already provides the domain context, so method names must describe only the action.
For example, inside RestaurantService:
Correct:
getAll()
getById()
create()
update()
delete()
changeStatus()Avoid:
getRestaurants()
getRestaurantById()
createRestaurant()
updateRestaurant()
deleteRestaurant()
changeRestaurantStatus()Use the same names for equivalent CRUD operations across all Services:
getAll()
getById()
create()
update()
delete()Use a different verb only when the operation represents a specific action.
For example:
cancel()
confirm()
complete()
changeStatus()TypeScript typing must be used consistently.
- Avoid
any. - Type function parameters.
- Type important function return values.
- Type Props and Emits.
- Type domain-related structures.
- Use type inference when the type is already clear.
- Do not add redundant types unnecessarily.
- Use specific types when only a limited set of values is valid.
Store Interfaces in:
src/interfaces/
Use them for objects, contracts, and domain entities.
Example:
export interface UserInterface {
id: number;
name: string;
role: UserRole;
}Naming examples:
UserInterface
RestaurantInterface
ReservationInterface
ReviewInterface
Use type mainly for unions, literals, states, roles, and limited sets of values.
Example:
type UserRole = 'client' | 'admin';
type Rating = 1 | 2 | 3 | 4 | 5;If a type belongs directly to an Interface, it may remain in the same file.
Store DTOs in:
src/dtos/
Use a DTO when an operation requires several related values.
Prefer:
create(dto: CreateReservationDTO)Instead of passing many individual parameters.
Example DTO names:
LoginDTO
CreateReviewDTO
CreateReservationDTO
UpdateRestaurantDTO
When an import is used only as a TypeScript type, use import type.
import type { Rating } from '@/interfaces/ReviewInterface';Imports must follow the same organization across the project.
- Separate external and internal imports.
- Keep each group in alphabetical order.
- Separate the groups with a blank line.
- Remove unused imports.
- Use
import typewhen the import is only used as a type.
Example:
import { defineStore } from 'pinia';
import { computed, ref } from 'vue';
import type { UserInterface } from '@/interfaces/UserInterface';
import { AuthService } from '@/services/AuthService';Use the following rules when deciding how a value should be handled.
Use a normal variable when Vue does not need to track its value.
Example:
const maxStars = 5;Use ref when a value can change and that change must update the UI.
Example:
const selectedRating = ref<Rating>(1);Use computed only for values derived from reactive data.
Example:
const fullName = computed(() => {
return `${firstName.value} ${lastName.value}`;
});A computed value must not produce side effects.
Do not use computed for:
- API calls.
- Writing to
localStorage. - Modifying other reactive values.
- Creating, updating, or deleting records.
- Redirecting the user.
Use watch when an action must happen because a reactive value changed.
Examples include:
- Calling an API.
- Saving information to
localStorage. - Running validation.
- Redirecting the user.
Do not use watch only to calculate derived data.
The decision can be summarized as:
Vue does not need to track the value
-> normal variable
The value changes and affects the UI
-> ref
The value is calculated from reactive data
-> computed
An action must run because reactive data changed
-> watch
Use Props to send information from a Parent to a Child.
Rules:
- Type all Props.
- Send only the data required by the Child.
- Do not modify Props directly inside the Child.
Example:
interface Props {
rating: Rating;
readonly?: boolean;
}
const props = defineProps<Props>();Use Emits to communicate actions from a Child to a Parent.
The Child reports what happened, while the Parent decides what to do with that information.
Example:
const emit = defineEmits<{
change: [rating: Rating];
}>();
emit('change', rating);Communication flow:
Parent
|
v
Props
|
v
Child
|
v
Emit
|
v
Parent
Use the same order inside the <script setup> block whenever the corresponding sections are required.
Do not create empty sections.
// 1. External imports
// 2. Internal imports
// 3. Props
// 4. Emits
// 5. Normal variables
// 6. Reactive variables
// 7. Computed
// 8. Selectors
// 9. Watchers
// 10. Methods
// 11. Lifecycle hooksExample:
<script setup lang="ts">
// Imports
import { computed, ref } from 'vue';
import type { Rating } from '@/interfaces/ReviewInterface';
// Props
interface Props {
rating: Rating;
}
// Emits
const emit = defineEmits<{
change: [rating: Rating];
}>();
// Normal variables
const maxStars = 5;
// Reactive variables
const selectedRating = ref<Rating>(1);
// Computed
const isMaximumRating = computed(() => {
return selectedRating.value === maxStars;
});
// Methods
function selectRating(rating: Rating): void {
emit('change', rating);
}
</script>Use this guide to determine where code belongs:
Complete application screen
-> View
Reusable UI
-> Component
CRUD operation or domain rule
-> Service
Global or shared state
-> Store
Reusable stateless technical logic
-> Util
Data required by a specific operation
-> DTO
Domain contract or data structure
-> Interface
Non-reactive value
-> normal variable
Reactive value that affects the UI
-> ref
Value derived from reactive data
-> computed
Action triggered by a reactive change
-> watch
Parent sends data to Child
-> Props
Child reports an action to Parent
-> Emits