POCO — выравнивание или использование сложных типов

Я использую POCO в качестве классов моделей в проекте ASP.Net MVC. Пока это работает хорошо, но в большинстве случаев эти POCO содержат простые типы. Теперь у меня есть ситуация, когда я рассматриваю возможность использования некоторых сложных типов, и я не уверен в преимуществах этого подхода. Далее моя ситуация осложняется тем, что мне нужно разделить одну таблицу базы данных на несколько POCO. Следующий пример объяснит. У меня есть таблица сотрудников с примерно 40 полями. Я не хочу всегда передавать этот большой объект со всеми его свойствами, поэтому я создал 3 класса POCO, которые представляют собой логические группировки данных следующим образом:

public class EmployeeProfile
{
    public int EmployeeID { get; set; }
    public string FirstName { get; set; }        
    public string LastName { get; set; }
    public string Gender { get; set; }
    etc ...    
}

public class EmployeeContact
{
    public Address Address { get; set; }
    public PhoneNumber Phone { get; set; }        
    public string Email { get; set; }
}

public class EmploymentInfo
{
    public decimal Salary { get; set; }       
    public string Occupation { get; set; }
    etc ....
}

Пока все хорошо, однако иногда мне нужна вся таблица сотрудников как единый объект, и в этом я не уверен. Я мог бы создать класс сотрудников, поместив в него другие типы следующим образом:

public class EmployeeDetail
{
    public EmployeeProfile EmployeeProfile { get; set; }
    public EmployeeContact EmployeeContact { get; set; }
    public EmploymentInfo EmploymentInfo { get; set; }        

}

или я мог бы скопировать все свойства, отдельные свойства трех POCO, включая содержащиеся в них сложные типы, в класс EmployeeDetail, чтобы он был плоским, с такими простыми свойствами:

public class EmployeeDetail
{
    public int EmployeeID { get; set; }
    public string FirstName { get; set; }        
    public string LastName { get; set; }
    public string Gender { get; set; }
    etc ...        
    public Address Address { get; set; }
    public PhoneNumber Phone { get; set; }        
    public string Email { get; set; }
    public decimal Salary { get; set; }       
    public string Occupation { get; set; }
    etc ....

}

помимо того факта, что версия, содержащая сложные типы, займет меньше времени для создания POCO, каковы преимущества использования одного подхода над другим? Я понимаю, что все дело в том, чтобы быть объектно-ориентированным, но я не уверен, есть ли в этом случае какая-либо польза от «вложения» этих объектов. Мне кажется, что будет больше работы по доступу к вложенным объектам и их переводу между моей моделью представления и моделью предметной области.

Редактировать. Я хотел бы отметить, что подмодели, которые я рассматриваю, не относятся к конкретному представлению. Скорее всего, они будут использоваться в нескольких местах приложения. Это приложение HR / льготы для сотрудников. Мне нужно, чтобы эти классы можно было использовать повторно, иначе я бы просто поместил их в ViewModel, которую уже использую.


person Louise Eggleton    schedule 04.02.2014    source источник


Ответы (3)


На самом деле вы хотите разделить свою бизнес-концепцию Employee на 3 части в зависимости от представлений, в которых они будут использоваться. Итак, вы хотите разделить его на EmployeeProfile, чтобы показать его в представлении, связанном, например, с управление профилями и т. д.

Поступая таким образом, вы смешиваете бизнес-домен с доменом просмотра, а этого вам следует избегать.

Разделение ваших моделей доменов — хорошее решение в случае логического разделения доменов + существует высокая вероятность того, что определенные части потребуются по отдельности. Таким образом, хорошим примером может быть (хотя и не обязательно в каждом случае) определение отдельной модели предметной области для Address из Employee.

Что бы я сделал на вашем месте:

  • Я бы оставил модель предметной области Employee, не разбивая ее на части, связанные с представлениями.
  • Я бы создал отдельные модели представлений в зависимости от того, какая часть данных модели Employee мне нужна для определенного представления.
  • Я бы подумал, если бы я мог разделить модель Employee на некоторые подмодели, связанные с доменом (например, отделить адрес от Employee).

С точки зрения производительности вышеуказанное решение также может быть полезным. Отправка нескольких дополнительных полей из базы данных и в базу данных не такая уж большая проблема, тогда как необходимость ПРИСОЕДИНЯТЬ свои подмодели, если вам нужна вся подмодель, в некоторых случаях может быть вредной для вашей производительности (особенно если есть тысячи сотрудников).

Более того: с точки зрения ремонтопригодности я бы также рекомендовал использовать вышеуказанное решение. Использование слишком большого количества вложенных объектов, когда это не совсем правильно с точки зрения предметной области, может создать некоторые дополнительные проблемы, например. CRUD-операции (например, потребуется предоставить механизмы фиксации/отката при сохранении/обновлении всего Employee и т. д.).

person Paweł Bejger    schedule 04.02.2014
comment
Я уже использую ViewModels. Моя точка зрения не в том, что мне нужны подмодели для представлений, а в том, что я не хочу постоянно проходить мимо этого класса свойств 40. Я думаю, вполне законно рассматривать часть модели Employee как законный аспект модели предметной области. Где сказано, что в домене должны быть только целые сущности? - person Louise Eggleton; 04.02.2014
comment
Я обновил свой ответ дополнительными пояснениями. - person Paweł Bejger; 04.02.2014
comment
В моем сценарии нет необходимости в присоединении к модели, потому что это делается в DAL. Я использую SQL-запросы для заполнения своих объектов. Таким образом, все свойства, которые мне нужны, поступают из одного запроса, и я заполняю все подмодели одновременно. - person Louise Eggleton; 04.02.2014
comment
Я отредактировал свой вопрос. Одной из причин разделения модели является повторное использование. Мне нужны отдельные классы в разных местах, а не только для одного конкретного вида. Это соответствует той части вашего ответа, в которой упоминаются подмодели, которые имеют смысл, когда существует высокий потенциал того, что определенные части потребуются отдельно. Это действительно так, и я не ясно дал понять это в своем первоначальном вопросе. - person Louise Eggleton; 04.02.2014

У @bejger есть отличный ответ, но я хотел добавить немного длинного комментария.

Разделять данные — плохая идея. Неважно, содержит ли ваша таблица 40 свойств или 400, вам всегда лучше выполнять запросы к одной таблице, чем выполнять соединения. Теперь, есть оговорки к тому, что я только что сказал. Следует разбивать логические группы данных, которые можно использовать повторно. Такие вещи, как адрес, лучше объединять в класс, которому нужна эта информация, чем повторять снова и снова.

Однако с композицией у вас есть два варианта:

  1. Сложный тип. Сложные типы позволяют абстрагировать набор свойств, которые вы хотите использовать в нескольких классах, но в конце концов каждая таблица получает свою собственную копию этих свойств. Преимущество этого заключается в том, что вам не нужно использовать соединения для доступа к данным, но есть ограничение, заключающееся в том, что вы не можете включать внешние ключи или другие свойства навигации.

  2. Внешний ключ. Использование внешнего ключа позволяет вам иметь одну таблицу для хранения всех экземпляров этой группы свойств. Опять же, недостатком является необходимость соединения. Это действительно подходящий вариант, только если вы можете предусмотреть взаимодействие только с этими свойствами или вам нужна их коллекция "один ко многим". Используя пример адреса, если вы хотите иметь возможность просто получить все адреса, не беспокоясь об ассоциации сотрудников, или вы хотите иметь возможность назначать сотруднику переменное количество адресов, то это жизнеспособный вариант.

Кроме этого, не беспокойтесь о размере вашего стола. Даже таблица с множеством столбцов по-прежнему более эффективна, чем соединение, и если вам нужно только подмножество этих столбцов, вы можете просто выбрать те, которые вам нужны (вы можете сделать это в LINQ с помощью метода Select, который Entity Framework будет учитывать при запросе базы данных).

person Chris Pratt    schedule 04.02.2014
comment
Пожалуйста, посмотрите на мою правку. Это действительно повторно используемые подмодели, которые требуются во многих местах моего приложения. Это не было ясно в моем первоначальном вопросе. - person Louise Eggleton; 04.02.2014
comment
Тогда у вас есть ответ. См. мое руководство о том, использовать ли сложный тип или внешний ключ, но вам понадобится один или другой, если вы хотите использовать их повторно. - person Chris Pratt; 04.02.2014

В конце концов, вместо того, чтобы разбивать классы модели предметной области на подкомпоненты, я применил совершенно другой подход и решил отделить мою модель чтения от моей модели предметной области или, другими словами, отделить R от CUD. Причина, по которой я это делаю, заключается в том, что 65-75% моего приложения связано с чтением и отчетом о данных, а не с их созданием, обновлением или удалением.

Таким образом, в этом случае я бы использовал в качестве объекта предметной области полную модель Employee без состава:

public class Employee
{
    public int EmployeeID { get; set; }
    public string FirstName { get; set; }        
    public string LastName { get; set; }
    public string Gender { get; set; }
    etc ...        
    public Address Address { get; set; }
    public PhoneNumber Phone { get; set; }        
    public string Email { get; set; }
    public decimal Salary { get; set; }       
    public string Occupation { get; set; }
    etc ....
}

В дополнение к этому на стороне чтения у меня может быть, например, следующее:

public class EmployeeRoster
{
    public int EmployeeID { get;set;}
    public string FullName { get;set; }   
    public decimal Salary { get;set; }       
    public string Occupation { get;set;}
    public DateTime DateOfHire { get;set;}
}

и любые другие объекты, которые мне нужны на стороне чтения. Это очень похоже на CQRS (разделение ответственности за запросы команд), и я уже давно знаю о CQRS, но ресурсы, которые я видел в CQRS, меня разочаровывали. Например, часто предлагается использовать разные базы данных для операций чтения и записи. Хотя я понимаю, почему кто-то может захотеть это сделать (производительность), для моих целей на данный момент это излишне. Кроме того, большинство ресурсов по CQRS предлагают напрямую сопоставлять запросы на чтение с моделями просмотра. У меня есть несколько проблем с этим. Во-первых, во многих случаях мне нужно повторно использовать эти модели чтения. Что-то вроде EmployeeRoster используется в нескольких местах моего приложения для разных целей. Кроме того, бывают случаи, когда мне нужно применить бизнес-логику к моим объектам чтения, прежде чем они будут использованы моделью представления.

Подводя итог, на данный момент я уверен, что мне нужна отдельная модель чтения, но я не обязательно убежден, что CQRS в том виде, в каком она официально практикуется, — это то, что нужно. Итак, на данный момент я ответил на свой собственный вопрос, но не принял его в качестве своего ответа, потому что я думаю, что другие люди с большим опытом работы с отдельными моделями чтения и/или CQRS могли бы дать лучший ответ, чем я.

person Louise Eggleton    schedule 18.02.2014