RuslanPr0g/Hiscaries

🚀 FRONTEND: Standardize Model Mapping Layers for Form, Domain, and API Communication

开放

#127 创建于 2025年5月23日

 (0 条评论) (1 个反应) (1 位负责人)C# (0 个派生)auto 404
enhancementhelp wanted

仓库指标

星标
 (2 个星标)
PR 合并指标
 (PR 指标待抓取)

描述

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.

贡献者指南