#c# #asp.net-identity #domain-model #rich-domain-model
#c# #asp.net-идентификация #предметная область-модель #богатая доменная модель
Вопрос:
Я пытаюсь разобраться в моделях расширенной предметной области и в том, как встроить семантическую функциональность в объекты домена, где объекты домена не тесно связаны с объектами, которые предоставляют реализации для семантического поведения
Например, я хочу встроить User
объект в свою модель предметной области, но я хочу, чтобы его реализация определялась identity framework
class User
{
public string Email { get; set; }
... All of the other IdentityUser properties...
public void DisableUser()
{
...behaviour to disable a user, most likely requires UserManager
}
public void AddToRole(Role role)
{
... most likely requires RoleManager
}
}
Итак, теперь, когда у меня есть модель предметной области, которая ведет себя в соответствии с бизнес-правилами и не имеет отношения к постоянству и реализации.
Но как именно DisableUser()
и AddToRole()
должны работать, когда они не имеют зависимостей и никоим образом не связаны с UserManager
и RoleManager
?
- В общем, чего мне не хватает?
- Должны ли объекты домена иметь зависимости от объектов, которые обеспечивают поведение?
- Как я должен отделить свою модель предметной области от поставщиков реализации?
Ответ №1:
Что я делаю, так это то, что каждая из моих объектов расширенной модели домена получает ссылку на центральный объект домена в качестве параметра конструктора и сохраняет его как readonly
элемент.
Это легко, потому что домен действует как фабрика своих сущностей, поэтому всякий раз, когда это new
одна из них, она передается this
в качестве первого параметра конструктора. (Предполагается, что объекты должны иметь внутренние конструкторы сборки, чтобы они не могли быть созданы кем-либо, кроме самого домена.)
И если вы действительно покопаетесь в документации фреймворков ORM, вы обычно обнаружите, что они, как правило, позволяют вам создавать фабрику для ваших сущностей, так что вы можете делать подобные вещи.
Итак, поскольку каждая сущность имеет ссылку на домен, она может получать от него все, что ей нужно для выполнения своей работы. (Предположительно, ваш доменный объект будет содержать ссылку на a UserManager
и на a RoleManager
, нет?) По сути, это прагматичный шаг назад от внедрения зависимостей: вы вводите объект домена с его зависимостями, но при этом каждый объект домена извлекает свои зависимости из объекта домена.
Вот пример на Java:
package ...
import ...
public final class StarWarsDomain extends Domain
{
private static final Schema SCHEMA = ...
public StarWarsDomain( LogicDomain logicDomain, S2Domain delegeeDomain )
{
super( logicDomain, SCHEMA, delegeeDomain ); //these get stored in final members of 'Domain'
}
public UnmodifiableEnumerable<Film> getAllFilms()
{
return getAllEntitys( Film.COLONNADE ); //method of 'Domain'
}
public Film newFilm( String name )
{
assert !StringHelpers.isNullOrEmptyOrWhitespace( name );
Film film = addEntity( Film.COLONNADE ); //method of 'Domain'
film.setName( name );
return film;
}
}
Комментарии:
1. Когда я делаю это, я называю свою богатую модель предметной области объектами предметной области и общей методологией предметно-ориентированного программирования , мне интересно, что другие думают об этом.
2. Не могли бы вы проиллюстрировать это на примере кода? (Ничего слишком большого, имейте в виду)
Ответ №2:
Хорошо разработанная модель предметной области не должна иметь зависимостей от каких-либо других архитектурных уровней или сервисов. Что касается, объекты модели предметной области должны быть (в моем случае) POCOs (обычные старые объекты CLR). Затем службы и уровни, такие как бизнес-логика или уровни сохраняемости, должны зависеть от этих объектов и возвращать их экземпляры.
Есть несколько ключей к построению модели предметной области, которая учитывает низкую связь, высокую согласованность и незнание постоянства. В одном заявлении секрет этого заключается в том, что «напишите код, который вы хотели бы иметь«.
Пример модели предметной области
public class Student
{
// Collections should be encapsulated!
private readonly ICollection<Course> courses;
// Expose constructors that express how students can be created.
// Notice that this constructor calls the default constructor in order to initialize the courses collection.
public Student(string firstName, string lastName, int studentNumber) : this()
{
FirstName = firstName;
LastName = lastName;
StudentNumber = studentNumber;
}
// Don't allow this constructor to be called from code.
// Your persistence layer should however be able to call this via reflection.
private Student()
{
courses = new List<Course>();
}
// This will be used as a primary key.
// We should therefore not have the ability to change this value.
// Leave that responsibility to the persistence layer.
public int Id { get; private set; }
// It's likely that students names or numbers won't change,
// so set these values in the constructor, and let the persistence
// layer populate these fields from the database.
public string FirstName { get; private set; }
public string LastName {get; private set; }
public int StudentNumber { get; private set; }
// Only expose courses via something that is read-only and can only be iterated over.
// You don't want someone overwriting your entire collection.
// You don't want someone clearing, adding or removing things from your collection.
public IEnumerable<Course> Courses => courses;
// Define methods that describe semantic behaviour for what a student can do.
public void Subscribe(Course course)
{
if(courses.Contains(course))
{
throw new Exception("Student is already subscribed to this course");
}
courses.Add(course);
}
public void Ubsubscribe(Course course)
{
courses.Remove(course);
}
}
Конечно, этот объект модели предметной области был написан с учетом Entity Framework, но он далек от обычных примеров Entity Framework (которые, напротив, являются анемичными моделями предметной области). Есть несколько предостережений, которые необходимо учитывать при создании объектов модели предметной области таким образом, но Entity Framework сохранит их (с небольшими хитростями), и вы получите объект модели предметной области, который определяет чистый семантический контракт для слоев, которые зависят от него.
Комментарии:
1. Это похоже на анти-шаблон «anemic domain model»