Друкарня від WE.UA

Мапери, або як написати багато коду заради чистої архітетури

Однією зі спорних тем в Clean Architecture як на мене це мапери. Сьогодні ми подивимось чому вони необхідні(або ні). Та реалізуємо простий мапер.

Що це і для чого

Почнемо з далека. Clean Architecture пропонує розділяти наш додаток на різні шари. І ці шари повинні бути максимально незалежні один від одного. Настільки незалежні що мають різні сущності для одних і тих самих обєктів в різних шарах.

Тобто якщо в нас є два шари, перший data layer а другий service layer. То в кожного з них буде окремий об’єкт для роботи

interface IServiceUser {
  id: string;
  name: string;
}

interface IDataUser {
  _id: string;
  name: string;
}

І ось тут ми вже бачимо проблеми. Як бути якщо поля в різних шарах у об’єкта повинні бути різні. Наприклад ми хочемо щоб данні зберігались в іншому вигляді ніж ми з ними працюємо.

Мапери

Ось тут ми і зустрічаємось з концепцією маперів. Мапер - це клас який надає дві функції для трансформації з одної форми в іншу.

Ось як би виглядав мапер для наших інтерфейсів

class UserMapper {

  from(entity: IServiceUser): IDataUser {
    return {
      _id: entity.id,
      name: entity.name
    }
  }

  to(entity: IDataUser): IServiceUser {
    return {
      id: entity._id,
      name: entity.name
    }
  }

}

Ніби все просто? Так воно і є. Концепт не є дуже складним. Тепер ми можемо використовувати цей клас для того щоб не турбуватись і однією функцією перетворювати об’єкт в нужний інтерфейс

Які проблеми?

Перше, очевидно такий підхід змущує нас писати додатковий код. Якщо ми пишемо не величкий додаток то це достатньо вагомо. А також якщо ми розділяємо різними інтерфейсами кожен з шарів то в звичайному Clean Architecture додатку який має 3 шара, ми будемо мати 3 інтерфейса і 2 мапера…..

…які треба підтримувати. По друге, важкість підтримування. Вам потрібно завжди тримати в голові що якщо ви змінюєте якись з інтерфейсів вам потрібно перевірити коректність роботи маперів.

Так а нашо вони потрібні якщо стільки мінусів?

Складне питання. Питання чи варто вводити мапери доволі хитке і залежить від ситуації. Моя рекомендація: якщо можно уникнути маперів, їх треба уникати. Але якщо для вашого сервісу важливо не залежити від жодних змін зовні і зберігати незалежність то варто їх додати.

Хто повинен викликати мапери?

Мапери повинен викликати той сервіс який залежить від другого. Тобто якщо сервіс А залежить від сервісу Б, то на його відповідальності перетворити обєкти через мапер на прийнятні для сервісу Б і передати їх. А потім відповідь знов ж таки перетворити за допомогою маперів на прийнятні для себе

class A {

  constructor(private B: Service, private mapper: Mapper) {}

  func(user: IUser) {
    const dataUser = this.mapper.to(user)
    const resultUser = this.B.someFunction(dataUser)
    const serviceUser = this.mapper.from(resultUser)
  }

}

Висновки

Висновки як завжди прозаічні. Мапери це той достатньо простий концепт який можно використовувати для відділення сущностей різних шарів один від одного. Я би радив уникати їх використання коли це можливо. Але знати як їх використовувати корисно

Статті про вітчизняний бізнес та цікавих людей:

  • CRM keyCRM: зручне рішення для продажів, комунікацій і керування командою

    Успіх компанії залежить від того, наскільки швидко вона здатна опрацьовувати вхідні запити. Коли дані про клієнтів розпорошені між різними месенджерами, виникає хаос. CRM keyCRM пропонує вихід із цієї ситуації, об’єднуючи всі робочі процеси в єдиному зручному інтерфейсі.

    Теми цього довгочиту:

    Crm
  • Різниця між UX і UI, яку варто зрозуміти ще до першого заняття

    Більшість людей, які обирають професію UX чи UI, довго вважають це одним і тим самим. Насправді це два різних підходи до роботи над продуктом, і плутанина між ними гальмує розвиток ще на старті.

    Теми цього довгочиту:

    Ui-ux
  • Логіка змін: як SEO оптимізація прибирає бар’єри до зростання

    Багато компаній приходять у SEO з очікуванням швидкого ривка, але дійсний ефект починається там, де сайт перестають латати точково. Тому в центрі роботи стоїть не окрема дія, а послідовні зміни. Оптимізація сайту має прибирати системні перешкоди, а не маскувати їх новими текстами

    Теми цього довгочиту:

    Seo
  • Музичний футуризм: неймовірні інструменти XXI століття

    Еволюція музичних інструментів це один із найкрутіших проявів потужності людської уяви і потреби виразити себе через мистецтво. І хоча багато традиційних інструментів майже не змінилися за століття існування, інновації і пошук не зупиняються.

    Теми цього довгочиту:

    Музичні Інструменти
  • Стіл – всьому голова? Так, якщо його правильно підібрати

    Коли починаєш вивчати пропозицію меблевих фабрик щодо столів, дивуєшся кількості варіантів, адже вони пропонують різні розміри, різні матеріали, різноманітні форми та дизайни. Скористайтесь нашим каталогом MebelOK, щоб Ви могли підібрати найкращу модель для Вашого приміщення

    Теми цього довгочиту:

    Столи
Поділись своїми ідеями в новій публікації.
Ми чекаємо саме на твій довгочит!
Владислав Лєсной
Владислав Лєсной@unnsin

5Довгочити
762Перегляди
17Підписники
На Друкарні з 15 квітня 2023

Більше від автора

  • Clear Architecture для бекенда на JS

    Кожен хто займається розробкою певний час приходить до слова архітектура. Сьогодні ми подивимось на одну з парадигм проектування архітектури через призму типового JS бекенду.

    Теми цього довгочиту:

    Clean Architecture
  • Паттерн “Репозиторій“ в JS

    В попередній статті ми розібрались з паттерном Специфікація Сьогодні ми поговоримо про логічне продовження паттерн репозиторій.

    Теми цього довгочиту:

    Js
  • Паттерн “Специфікація“ в JS чи потрібен він?

    Одним з найскладніших для мене був паттерн специфікація. Сьогодні я вам пояснити для чого він. І запропонувати свою версію реалізацію

    Теми цього довгочиту:

    Js

Це також може зацікавити:

Коментарі (3)

Цікаво, дякую!

dovgochyt request:

Що робити коли ти отримав об’єкт типу User і тепер хочеш цьому юзеру додати новий філд. Наприклад age:32

У юзера такого поля немає, але є інтерфейс умовно UserWIthAge;

Як з юзера зробити тепер UserWIthAge. Може я тупий, але я роблю щось типу:

Це також може зацікавити: