Перейти к основному содержимому

6.11. Конструкция из классов

Разработчику Архитектору Аналитику

Конструкция из классов

Буквально - нужно по папочкам всё разложить - в этом структурировании и вся суть грамотности

Так, мы рассмотрели различные типы классов. Как их правильно проектировать? Разумеется, пропускаем многие этапы, и фокусируемся на коде.

Определив нужные классы, мы выбираем наиболее точное имя и комбинируем его, к примеру, если есть Service, до можно сделать UserService. К примеру, UserEmailNotifier, UserModel, UserHelper и так далее. Затем можно свериться с паттернами, и только потом работать.

Какие же классы нам определять?

  1. Определяем класс - точку входа, к примеру, это Main, Application, Bootstrapper, Program.
  2. Определяем классы-интерфейсы (абстракцию):
    • описывающие поведение (контракты) - Interface, Contract, Service, Repository, DAO;
    • создающие объекты - Factory, Builder;
    • реализующие алгоритмы - Strategy;
    • обрабатывающие события - Listener, Observer, EventListener;
    • выполняющие операции как объект - Command;
    • проверяющие бизнес-правила - Specification, Validator, Rule;
    • передающие данные между слоями - Mapper, Transformer, Converter, DTO.
  3. Определяем классы, которые реализуют сервис или логику:
    • бизнес-логика - ServiceImpl, Service, UseCase, DomainService;
    • работа с данными - RepositoryImpl, DAOImpl, UnitOfWork;
    • валидация данных - Validator, RuleEngine, SpecificationValidator;
    • авторизация/аутентификация - AuthService, Authenticator, Authorizer, TokenManager;
    • логирование - Logger, AuditLogger;
    • обработка ошибок - ExceptionHandler, ErrorHandler, Notifier.
  4. Определяем классы, ответственные за обработку событий и сообщений:
    • слушающие события - EventListener, MessageConsumer;
    • отправители - EventDispatcher, MessageProducer;
    • обработчики - RequestHandler, TaskRunner, Processor.
  5. Определяем классы, которые работают с внешними системами:
    • внешний клиент - HttpClient, Client, Integrator, ExternalService;
    • отправка уведомлений - Mailer, SmsSender, Notifier;
    • управление кэшем - CacheManager, Cacher, CachedService.
  6. Определяем классы-модели предметной области:
    • сущности с ID - Entity, User, Order, Customer;
    • объекты без ID - ValueObject, VO, Address, Money;
    • корень агрегата - AggregateRoot, OrderAggregate;
    • представление UI - Model, ViewModel, Presenter, View.
  7. Определяем классы для работы с данными:
    • парсеры - Parser, JsonParser, XmlParser;
    • форматтеры - Formatter, DateFormatter, CurrencyFormatter;
    • преобразование - Mapper, Transformer, Converter;
    • загрузка/сохранение - Loader, Saver, Importer, Exporter;
    • агрегация данных - Aggregator, ReportGenerator, Analyzer.
  8. Определяем классы для работы с инфраструктурой:
    • управление с соединениями - ConnectionManager, Pool, DataSource;
    • миграции - Migrator, SchemaManager;
    • кэширование - CacheManager, CachedService;
    • шедулеры (планировщики) - Scheduler, TaskScheduler, JobRunner.
  9. Определяем классы для тестирования:
    • юнит-тестирование - Spec, Test, UnitTest;
    • подготовка данных - Fixture, Mocker, Stub, Spy;
    • запуск тестов - TestRunner, TestSuite.
  10. Определяем утилиты и помоники:
    • вспомогательный функционал - Helper, Utils, Extensions, Wrapper;
    • конфигурация - Config, Configurator, Settings;
    • инициализация - Bootstrapper, Initializer.

Разумеется, может быть различный набор комбинаций всех этих классов, так что такое разделение весьма условно. Сначала потребуется нарисовать схему, расставить логические элементы (узлы), которые важно будет рассмотреть с точки зрения необходимости декомпозиции. Начиная проектировать, сначала нужно все продумать на начальных верхних уровнях, потом спускаться по иерархии, разделяя по надобности.

Когда будут понятны этапы, компоненты уже создаются по формуле:

<A>+<B>+<C>

где:

  • A - контекст/объект - что это, о ком или о чём класс (User, Book, Order, Payment);
  • B - роль/назначение - зачем нужен, какую задачу выполняет (Service, Repository, Validator, Sender);
  • C - тип/паттерн - какой тип класса или паттерн (Impl, Adapter, Helper, Handler).

Примеры:

  • UserService - контекст User, роль Service;
  • PaymentValidator - контекст Payment, роль Validator;
  • LoggerAdapter - контекст Logger, роль и тип - Adapter.

Конечно, длинных имён ради точности делать не стоит, но и не нужно сокращать до невнятности (UsrSrv будет не так понятно, как UserService). Чем конкретнее, тем лучше, и когда паттерны явно применяются, лучше прямо это указывать в названиях. Порядок элементов можно и менять, но очевидно ServiceUser воспринимается немного иначе.