RuslanPr0g/Hiscaries
🚀 FRONTEND: Standardize Model Mapping Layers for Form, Domain, and API Communication
Open
#127 opened on May 23, 2025
enhancementhelp wanted
Repository metrics
- Stars
- (2 stars)
- PR merge metrics
- (PR metrics pending)
Description
Summary
We currently have several representations of models scattered across reactive forms, domain logic, and API integration. There is inconsistency in how these models are translated, leading to fragile code, duplication, and tight coupling between layers. This issue proposes introducing a structured, layered model mapping approach.
Problem
- Mixing of API and domain models in components and services.
- Reactive forms are directly bound to raw API objects.
- Lack of standardized mappers introduces duplication and inconsistent transformations.
- Difficult to scale or refactor form structures or API payloads independently.
Proposal
Introduce a clear separation of models and centralized mappers for each transformation step.
Layers
- Form Model: Used in Angular Reactive Forms (
FormGroup) File:{entity-name}-form.model.ts - Domain Model: Used in internal logic / UI (
{entity-name}class) File:{entity-name}-domain.model.ts - API Models: Input/output contract with backend
Files:
{entity-name}-api-read.model.ts,{entity-name}-api-request.model.ts
Mappers
Use stateless utility classes (pure functions) to transform between models.
Mapper Example:
// story.mapper.ts
export class StoryMapper {
static fromApiToDomain(api: StoryApiReadModel): StoryDomainModel
static fromDomainToForm(domain: StoryDomainModel): StoryFormModel
static fromFormToDomain(form: StoryFormModel): StoryDomainModel
static fromDomainToRequest(domain: StoryDomainModel): StoryUpdateRequest
}
Usage in Component:
// Loading for Edit
this.storyService.getById(id).pipe(
map(StoryMapper.fromApiToDomain),
tap(domain => {
const formModel = StoryMapper.fromDomainToForm(domain);
this.form.patchValue(formModel);
})
);
// Saving
onSubmit() {
const formModel = this.form.getRawValue();
const domainModel = StoryMapper.fromFormToDomain(formModel);
const request = StoryMapper.fromDomainToRequest(domainModel);
this.storyService.update(request).subscribe();
}
Benefits
- Separation of concerns — Forms, business logic, and API layers are decoupled.
- Scalability — Changes in backend contracts or form structure don’t leak into other layers.
- Testability — Mappers can be tested independently with full coverage.
- Consistency — Reduces bugs from improper or inconsistent data transformations.
Priority
High — This change lays foundational structure needed for upcoming feature expansion.