# Сущность (Entity) Класс _Сущность_ отвечает за какой-то реальный объект. Это может быть договор, стул, агент недвижимости, пирог, утюг, кот, холодильник - всё что угодно. Любой объект, который может вам понадобиться для моделирования ваших бизнес-процессов, - это _Сущность_. Понятие _Сущности_ по Эвансу и по Мартину отличаются. С точки зрения Эванса, сущность - это объект, характеризующийся чем-то, что подчеркивает ее индивидуальность. > Если объект определяется уникальным индивидуальным существованием, а не набором атрибутов, это свойство следует с читать главным при определении объекта в модели . Определение класса должно быть простым и строиться вокруг непрерывности и уникальности цикла существования объекта. Найдите способ различать каждый объект независимо от его формы или истории существования. С особым вниманием отнеситесь к техническим требованиям, связанным с сопоставлением объектов по их атрибутам. Задайте операцию , которая бы обязательно давала неповторимый результат для каждого такого объекта, - возможно, для этого с объектом придется ассоциировать некий символ с гарантированной уникальностью . Такое средство идентификации может иметь внешнее происхождение, но это может быть и произвольный идентификатор, сгенерированный системой для ее собственного удобства. Однако такое средство должно соответствовать правилам различения объектов в модели. В модели должно даваться точное определение, что такое одинаковые объекты. С точки зрения Мартина, _Entity_ - это не объект, а слой. Этот слой объединят как объект, так и бизнес-логику по его изменению. > My view of Entities is that they contain Application Independent Business rules. They are not simply data objects. They may hold references to data objects; but their purpose is to implement business rule methods that can be used by many different applications. > Gateways return Entities. The implementation (below the line) fetches the data from the database, and uses it to construct data structures which are then passed to the Entities. This can be done either with containment or inheritance. > > For example: > > public class MyEntity { private MyDataStructure data;} > > or > > public class MyEntity extends MyDataStructure {...} > > And remember, we are all pirates by nature; and the rules I'm talking about here are really more like guidelines... Мы под _Сущностью_ будем иметь в виду только структуру. В простейшем варианте класс _Entity_ будет выглядеть так: ```ruby module Entities class MeatBag < LunaPark::Entities::Simple attr_accessor :id, :name, :hegiht, :weight, :birthday end end ``` Мутабельный объект, описывающий структуры бизнес модели, может содержать примитивные типы и _Значения_. Класс [`LunaPark::Entites::Simple`](https://github.com/am-team/luna_park/blob/master/lib/luna_park/entities/simple.rb) невероятно прост, вы можете посмотреть его код, он дает нам только одну вещь - легкую инициализацию. ```ruby module LunaPark module Entities class Simple def initialize(params) set_attributes params end private def set_attributes(hash) hash.each { |k, v| send(:"#{k}=", v) } end end end end ``` Вы можете написать: ```ruby john_doe = Entity::MeatBag.new( id: 42, name: 'John Doe', height: '180cm', weight: '80kg', birthday: '01-01-1970' ) ``` Как вы уже наверное догадались вес, рост и дату рождения мы хотим обернуть в _Объекты-значения_. ```ruby module Entities class MeatBag < LunaPark::Entites::Simple attr_accessor :id, :name attr_reader :heiht, :wight, :birthday def height=(height) @height = Values::Height.wrap(height) end def weight=(height) @height = Values::Weight.wrap(weight) end def birthday=(day) @birthday = Date.parse(day) end end end ``` Чтобы не тратить время на подобные конструкторы, у нас подготовлена более сложная _Реализация_ [`LunaPark::Entites::Nested`](https://github.com/am-team/luna_park/blob/master/lib/luna_park/entities/nested.rb) : ```ruby module Entities class MeatBag < LunaPark::Entities::Nested attr :id attr :name attr :heiht, Values::Height, :wrap attr :weight, Values::Weight, :wrap attr :birthday, Values::Date, :parse end end ``` Как можно догадаться из названия, данная _Реализация_ позволяет делать древовидные структуры. Давайте удовлетворим мою страсть к крупногабаритной бытовой технике. В прошлой статье мы проводили аналогию между ["крутилкой" стиральной машины и архитектурой](https://habr.com/post/429750). А сейчас мы опишем такой важный бизнес-объект как холодильник: ![Refregerator](https://habrastorage.org/webt/xl/65/pj/xl65pjsr5ou5a4nurhdm9qxbo50.png) ```ruby class Refregerator < LunaPark::Entites::Nested attr :id, attr :brand attr :title namespace :fridge do namespace :door do attr :upper, Shelf, :wrap attr :lower, Shelf, :wrap end attr :upper, Shelf, :wrap attr :lower, Shelf, :wrap end namespace :main do namespace :door do attr :first, Shelf, :wrap attr :second, Shelf, :wrap attr :third, Shelf, :wrap end namespace :boxes do attr :left, Box, :wrap attr :right, Box, :wrap end attr :first, Shelf, :wrap attr :second, Shelf, :wrap attr :third, Shelf, :wrap attr :fourth, Shelf, :wrap end attr :last_open_at, comparable: false end ``` Такой подход избавляет нас от создания ненужных _Сущностей_, таких как дверь от холодильника. Без холодильника она должна быть частью холодильника. Такой подход удобен для составления сравнительно больших документов, например заявка на покупку страховки. У класса `LunaPark::Entites::Nested` есть еще 2 важных свойства: Сравнимость: ```ruby module Entites class User < LunaPark::Entites::Nested attr :email attr :registred_at end end u1 = Entites::User.new(email: 'john.doe@mail.com', registred_at: Time.now) u2 = Entites::User.new(email: 'john.doe@mail.com', registred_at: Time.now) u1 == u2 # => false ``` Два указанных пользователя не эквивалентны, т.к. они были созданы в разное время и поэтому значение атрибута `registred_at` будет отличаться. Но если мы вычеркнем атрибут из списка сравниваемых: ```ruby module Entites class User < LunaPark::Entites::Nested attr :email attr :registred_at, comparable: false end end ``` то получим два сопоставимых объекта. Эта _Реализация_ так же обладает свойством оборачиваемости - мы можем использовать метод класса`wrap ```ruby Entites::User.wrap(email: 'john.doe@mail.com', registred_at: Time.now) ``` Вы можете использовать в качестве _Entity_ - Hash, OpenStruct или любой понравившийся вам gem, который поможет вам реализовать структуру вашей сущности. _Сущность_ - это модель бизнес объекта, оставьте ее простой. Если какое-то свойство не используется вашим бизнесом, не описывайте его.