I'm trying a project using DDD. I'm confused on how to proceed. Here is a sample of my Entities. Tried to keep it brief.
But I'm struggling with the following
1. Update an address on a Person - there are business rules such as the Person must have X number of Address type.
I can grab the address from Person.Addresses and change it, but how would I validate Person is still valid when the changes were on the address?
2. If 2 addresses are required on creation. What would be the best way to to initialize the object? There could be more addresses on creation as well.
public class Person // AggregateRoot
{
public Guid Id { get; set; }
public Guid StatusId { get; set; } //FK to Status
private List<Address> _addresses = new List<Address>();
public IReadOnlyCollection<Address> Addresses => _addresses.AsReadOnlyList();
//more
public void AddAddress(...)
{
//Logic
_addresses.Add(...)
}
}
public class Status
{
public Guid Id { get; set; }
public string Name { get; set; }
//more..
}
public class Address()
{
public Guid Id { get; set; }
public string Address1 { get; set; }
public Guid AddressTypeId { get; set; }//FK to Address Type
}
public class AddressType()
{
public Guid Id { get; set; }
public string Name { get; set; }
}
The typical approach here is to control any operations on the child entities via the parent. In DDD parlance, the two entities working together is an aggregate and the Person entity is the aggregate root. All operations on the Addresses should be done via the Person - this gives the Person class the opportunity to enforce the invariants (rules) that apply even across the collection of Addresses.
As an example:
class Person
{
void UpdateAddress(Guid addressId, ...updatevalues)
{
var address = Addresses.Select(a => a.Id == addressId);
address.Update(updateValues);
VerifyAddressesCorrect();
}
}
The code outside the Person class should never call any methods on the Address directly.
To answer your second question, a common pattern would be to put the rules in a constructor overload, or if that doesn't suit, a static factory method on the Person class. This gives you a lot of flexibility for how you structure the input and what rules you enforce at the time the entity comes into existance.
Personally I like the static factory method because it allows you to give a more meaningful name to what is going on when the class is instantiated. I try and avoid terms like Create because there's usually a more domain oriented term, like Person.Register() or Person.Onboard()
Similarly, the .UpdateAddress method is not great as Update is a crud term that doesn't shed much light on the domain - what is actually happening here from the user's/domain perspective? Sure, sometimes an update is just an update (e.g. a data entry error needs correcting), but it can be a prompt to consider if there is a more meaningful domain concept at play.
Related
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 2 years ago.
Improve this question
I'm always struggling with naming convention and design pattern for my application, it's been very hard to keep it consistent, so I have a simple case, let's say I have a service with method called CreateOrder
public OrderDTO CreateOrder(int customerID, OrderShipDTO shipping, OrderDetailDTO products);
Here's the DTO class
public class OrderDTO
{
public int ID { get; set; }
public decimal PriceTotal { get; set; }
public string Status { get; set; }
public string PaymentMethod { get; set; }
public OrderShipDTO OrderShip { get; set; }
public ICollection<OrderDetailDTO> OrderDetails { get; set; }
}
public class OrderShipDTO
{
public string Name { get; set; }
public string Phone { get; set; }
public string Address { get; set; }
public string Province { get; set; }
public string City { get; set; }
public string District { get; set; }
public string SubDistrict { get; set; }
public string ZipCode { get; set; }
}
public class OrderDetailDTO
{
public int ID { get; set; }
public decimal Quantity { get; set; }
public decimal Price { get; set; }
public int ProductID { get; set; }
}
As you can see, my method CreateOrder will return OrderDTO and accept OrderDetailDTO parameters, but the method actually only requires property OrderDetailDTO.ProductID and OrderDetailDTO.Quantity for the business logic calculation.
So, it feels not right for me (and confusing because I wasn't sure which properties need to have a value and which doesn't) to pass the entire OrderDetailDTO object even though it only needs 2 of the properties to be filled, but I still need to pass back the OrderDTO which will include ICollection<OrderDetailDTO> because I need to get the OrderDetailDTO.Price value and show it to my customer.
So I was thinking of creating another DTO like this
public class OrderDetailDTO_2 //temp name
{
public decimal Quantity { get; set; }
public int ProductID { get; set; }
}
But I will end up with a lot of DTOs and even though I'm fine with it, what's the best practice for the DTO naming?
I don't see the point (from the posted info) in having an ID and a ProductId in an orderdetail because the ProductId should be sufficient; if the user adds 2 apples and then adds 3 apples, you can just set the order quantity to 5 apples for the single apples line, rather than distinctly track 2 apples vs 3 apples lines
I don't see the point in not having the price in the orderdetail; items always have a price. It might change or not, but there's no harm in communicating the price to the front end with every back and forth- the front end doesn't have to remember anything then, leading to my next point:
I also don't see the point in having the total in the order unless it is somehow ever going to be different to the sum of all the detail quantity * price. The client can do sums just like the server can. If the client knows the quantity and price, have it work out the total itself
I don't think OrderShip is well named. OrderShip is actually an Address, and looks like it could have plenty of uses in other parts of the program like a billing address, invoicing address, correspondence address. Name your objects according to what they are rather than what they are for - use the name of the variable to indicate what it is for:
public AddressDto ShippingAddress ...
public AddressDto BillingAddress ...
I was thinking of creating another DTO like this
Do not ever create a class name and just whack a 2 on it "because you couldn't think of a better name" - it is absolutely zero help in letting another developer (or yourself by the time you've forgotten this project) know the difference. Hands up anyone who can tell me every difference between an oracle sql VARCHAR and VARCHAR2 without hitting the manual (Gordon, this one's for everyone else ;) )
the method actually only requires property OrderDetailDTO.ProductID and OrderDetailDTO.Quantity for the business logic calculation.
You didn't post any code but there is a reasonable point in the comments that classes can inherit; the base class can have the common properties all classes have, the subclass can have more properties, plus it gets the base ones. The client could send a base class "I want to order apples, 3" and get a subclass "order item apples, 3, $1"
I'm not sure that these arguments warrant creating a whole new class just to dispense with 1 property though. I'd just reuse the same class and sometimes its price is filled in (server to client) and sometimes not/doesn't have to be/ignored (client to server, don't want client setting the prices!)
so I have a simple case, let's say I have a service with method called CreateOrder
Creating an order isn't what I'd call a simple case; it wouldn't need an SO question if it were - your create order method doesn't seem to ask for any payment related argument but the order dto tracks it, so I'm wondering if that is missed off? It's also something I would expect to be done at the end unless you're perhaps allowing the customer to build multiple baskets gradually, and the orderid is generated at the start as a basket reference (in which case perhaps there is a separate process for adding payment info)
At the end of all this, you perhaps need to sit down and map out your workflows first and what data they will need and aim to strike a balance between reusing one mega dto that tracks everything vs having a dto for every case and ending up with 1000 DTOs. These are the two extremes and you will almost never go there. There is always some element of reuse that can and should be applied to limit the maintenance headache. Feel free to inherit classes if they have sensible common base elements, but I wouldn't recommend you have a base OrderDto with the order Id and then DTOs for createorder, updateorder, cancelorder, addordershippingaddress, changeordershippingaddress, changeorderbillingaddress, reportorderreceived, reportordernotreceived etc - most of those operations only need an order Id, maybe an address detail or order detail; you can have a couple of dto; the base order (for operations like cancel, report received etc) and the full one (with null billing address if you're changing the shipping address).
You can use the name of the method being called to know whether it's the billing or shipping you need to change, or the client can just send a pair of modified addresses (both addresses need updating), a modified and unmodified address (update one address but not the other), a null and nonnull address (remove one address but not the other) ... and the server can process them all in the same way: the new address pair is the truth; overwrite the old data with the new.
This has struck a balance between the dto extremes; if we happen upon a situation where neither dto applies, then we can consider making another if no existing one is ideal. We shouldn't use fields for things other than which they were intended (don't use the credit card number field to store the phone number if they person pays by bank transfer) even if it is "just that one time" - perhaps adding a phone number would be an opportunity to extend the Address dto and call it something different like ContactDetail
At first you have to know your business logic and the required business entities. Without knowing any details about the application's business, the business logic can always be viewed as a black box, which requires input, processes this input and produces output i.e. a result (IPO model).
With the IPO model in mind, you can now design your business entities around the business logic. Identify the business process, derive the logic and design the required entities (their behavior or attributes and relationships). Start to design interfaces to describe those entities.
Note that it is not the goal to have a dedicated object for each method, so that each object only exposes the data the method needs. Although interface segregation is a good approach to achieve this, by encapsulating data contexts or responsibilities.
Since you have expressed your focused on naming conventions, also note that there is no value in adding a prefix or suffix to the type name that describes their physical (e.g. bool) or logical (e.g., DTO) data type. There is absolutely no value in this naming style. It only makes the names ugly and degrades readability. This is the main reason why e.g., Hungarian Notation is obsolete today.
Example
I assume that your business logic's goal is to create an actual order based on the data input provided by a user. This example of course is based on a very simplified process.
You can always use interface segregation to encapsulate responsibilities. An object that implements fine grained interfaces, can be passed around by this interfaces, allowing fine grained context related exposition of attributes.
Data input business entities
interface IItem
{
decimal Quantity { get; set; }
int ProductID { get; set; }
}
// Describes data needed for shipping related to a customer
interface IShippingAddress
{
string Address { get; set; }
string Province { get; set; }
string City { get; set; }
string District { get; set; }
string SubDistrict { get; set; }
string ZipCode { get; set; }
}
// Consolidates data needed to create an order.
// Provided by customer order process.
interface IPurchase
{
int CustomerId { get; set; }
IShippingAddress ShippingAddress { get; set; }
IEnumerable<IItem> Items { get; set; }
}
Data output business entities
// Result entity that extends IITem to add internal details like price.
interface IOrderItem : IItem
{
int Id { get; set; }
decimal Price { get; set; }
}
// Encapsulates contact details
interface ICustomerContactInfo
{
string Phone { get; set; }
string EmailAddress { get; set; }
}
// Encapsulates customer details like IsVip, or contact info etc
interface ICustomerInfo : ICustomerContactInfo
{
int CustomerId { get; set; }
string Name { get; set; }
}
// Consolidates the data needed for the shipping process
interface IShippingInfo : ICustomerInfo, IShippingAddress
{
}
// Consolidates and adds data needed for managing the order's delivery process
interface IDelivery : IShippingInfo
{
bool IsInDelivery { get; set; }
DateTime PickupDate { get; set; }
DateTime DueDate { get; set; }
}
// The result entity
interface IOrder
{
int Id { get; set; }
decimal PriceTotal { get; set; }
string Status { get; set; }
string PaymentMethod { get; set; }
IDelivery DeliveryInfo { get; set; }
ICollection<IOrderItem> Items { get; set; }
}
Business logic
public IOrder CreateOrder(IPurchase purchase)
{
// Get the result item info that contains actual price etc
ICollection<IOrderItem> orderItems = GetOrderItems(purchase.Items);
ICustomerInfo customer = GetCustomer(purchase.CustomerId);
IShippingInfo shippingInfo = CreateShippingInfo(purchase.ShippingAddress, customer);
IOrder result = CreateOrderItem(orderItems, shippingInfo);
return result;
}
public void SendConfimationMail(ICustomerContactInfo contactInfo)
{
SendMailTo(contactInfo.EmailAddress, message);
}
public void OrderDeliveryService(IShippingInfo shippingInfo)
{
SubmitDeliveryOrder(shippingInfo);
}
Example
public static Main()
{
IPurchase purchase = CollectOrderDataFromUser();
IOrder orderItem = CreateOrder(purchase);
// Only pass the ICustomerContactInfo part of IDelivery as argument
SendConfimationMail(orderItem.DeliveryInfo);
// Only pass the IShippingInfo part of IDelivery as argument
OrderDeliveryService(orderItem.DeliveryInfo);
}
The degree of segregation or the design of the interfaces may not make much sense for you. But the goal of this example is to provide a raw and basic example on how to design business entities, that reflect the business logic, which reflects the real life business process. It shows how interface segregation helps to pass around only a specific context (interface) of an object to enforce encapsulation or to limit greediness.
I have studied many articles about how to config one to one relationships. I have learned it.
But I couldn't understand how to find a dependent entity in one to one relationship?
For instance, we have two entities User and Developer. How to understand which of them is a dependent entity? Because I think we must add a foreign key to a dependent entity. So the first things to do, we need to find a dependent entity.
public class User
{
public int Id {get;set;}
public string PasswordHash { get; set; }
public string FullName { get { return FirstName + " " +LastName;}}
public GenderType Gender { get; set; }
public bool IsActive { get; set; }
public DateTimeOffset LastLoginDate { get; set; }
public string FirstName { get ; set ; }
public string LastName { get; set; }
}
public class Developer
{
public int Id { get; set; }
public byte Image { get; set; }
public int Age { get; set; }
public string Resume { get; set; }
}
A dependent is something that depends on something else. A baby is dependent on its mother for food etc. Identify which entity can stand alone, without the other.
For example, you may decide that a User might not be a Developer but a Developer is always a User - in this case your relationship is actually 1:0..1 (user:developer) and you're looking at Developer being a subclass of a User. If. You could alternatively arrange things in a has-a fashion, and Developer has a User property (but User doesn't have a Developer property because not every User is a developer)
You may decide that you can never have one without the other- in which case they would probably be a good candidate for being in the same table/same client side entity
To some extent the question can be academic; there may be situations where you want to treat one as dependent, and others where it's the inverse way round. It'll probably help you overall though if you make a decision about how your entities are related on the client side and this will drive how you map them on the database side
This is for the first time I am implementing the concept of Domain Driven Design in a real project. The project is about generating a permit for employees which would enable them to enter company premises. Through an Intranet site, an employee who already possesses an Employee ID, will be able to apply for a permit or apply for replacement of a permit, in case the permit is damaged or lost. And an administrator will process the applications and hand over a physical permit in the end.
I have a couple of entities - User, Permit and one Value Object - Employee.
A User can be entirely identified by an Employee. Should I create a UserId as well? How do I handle the case in which the administrator may not only approve, but raise an application on behalf of an employee as well?
The Value Object Employee has an Id property EmployeeId which I know goes against the principle of a Value Object. However, in my case, Employee is read-only. It doesn't matter to me if I get only the EmployeeId or the Employee as a whole. Am I doing this right?
The Permit entity contains a lot of properties. How do I break down the properties of Permit into Value Objects?
public class User : Entity
{
public Employee Employee { get; set; }
public bool IsAdmin { get; set; }
}
public class Permit: Entity
{
public int ApplicationId { get; set; }
public string EmployeeId { get; set; }
public string Type { get; set; }
public string Status { get; set; }
public DateTime RequestDate { get; set; }
public DateTime IssuanceDate { get; set; }
public string IssuanceReason { get; set; }
public string SurrenderReason { get; set; }
}
public sealed class Employee : ValueObject<Employee>
{
public string EmployeeId { get; private set; }
public string Name { get; private set; }
public string Department { get; private set; }
public string Division { get; private set; }
public string BirthDate { get; private set; }
public string Designation { get; private set; }
public string BloodGroup { get; private set; }
public string SecurityLevel { get; private set; }
public string EmploymentDate { get; private set; }
}
Apart from the concerns, I'd be grateful if someone could guide me to go about it in the most appropriate manner possible. Thanks.
In modeling business processes, it will often help to think about the paperwork, rather than the things in the real world. The process begins when somebody files a request document, it ends when the adminstrator files an approval or a rejection, and so on. The domain model's job is to do the bookkeeping, and to perform some of the logic on behalf of the business.
See
Rinat Abdullin: Evolving a Process Manager...
Greg Young: Stop Over Engineering
Should I create a UserId as well?
If you need to handle the case where the person submitting the request is different from the person the permit is assigned to, then you should absolutely treat those as different values.
It doesn't matter to me if I get only the EmployeeId or the Employee as a whole. Am I doing this right?
Yes - there is nothing wrong with an immutable value having an identifier.
In more sophisticated models, value objects will often be composed from other value objects. For example, event though EmployeeId and BloodGroup are opaque character sequences, it's probably not OK to pass a blood group to a method that is expecting an employee id, or vice versa. So rather than leaving things "stringly typed", we might invest more work in the model to make those concepts explicit.
How do I break down the properties of Permit into Value Objects?
Again, values can be composed from other values. If there is a subset of cohesive properties, group them together.
Scott Wlaschin's Domain Modeling Made Functional is a great reference here.
Part of the point of "objects" is that we should be able to change the in memory data structures and still have everything "work". So you shouldn't feel like you are committed to a particular in memory layout.
(Note: it's really easily to couple your in memory layout to your persistent storage -- that will make things harder to change later; so try not to fall into that trap more than once.)
From my point of perspective it would be better to discard the User class, as it closely resembles the Employee class.
The property IsAdmin is a very hard constraint, from a broader perspective you could create an Enum that represents security groups. This enables you to implement security for certain areas where given group X has access and group Y does not. This could replace the string SecurityLevel in the Employee class. You could also implement these security groups in the Permit class.
The Permit class should be used by the Employee class. A user can have multiple permits, active and non-active. A boolean value defining that logic should be implemented into the Permit class. By implementing the active/non-active boolean in the Permit class, you could revoke access by simply switching the value of the boolean.
I have a situation like the following toy example:
public class Person
{
public Guid Id { get; set; }
public string FullName { get; set; }
public ContactInfo ContactInfo { get; set; }
}
public class ContactInfo
{
public string EmailAddress { get; set; }
public IEnumerable<Address> PostalAddresses { get; set; }
}
public class Address
{
public Guid Id { get; set; }
public Guid PersonId { get; set; }
/* ... other properties ... */
}
The real situation is much more complex and I would really like the equivalent of the PostalAddresses property to be on ContactInfo instead of Person.
Is this possible using Entity Framework Code First?
EDIT
Basically, I want the above to be serialized into the following table structure:
table: People
Id
FullName
ContactInfo_EmailAddress
table: Address
Id
PersonId
...
So the problem is that I need to have the IEnumerable<Address> collection to be on the ContactInfo complex type instead of the root type, Person. When I save a Person object, no Address rows are added to the database.
Your question seems perfectly clear.
I'm not an expert, but according to How to specify a relation on a complex type? what you're trying to do is not possible. A ComplexType cannot have navigational properties.
I guess the workaround is to define ContactInfo as an entity, instead of a complex type. So ContactInfo will have a PK and it's own table.
Maybe somebody else knows a better solution?
By the way, I tried to do something similar. My (simplified) domain:
Application
Id : int
Candidate : Candidate
Resume : Resume <== I prefer to not have a separate table for this class, but how to achieve this??
Resume
Experiences : [Experience]
Trainings : [Training]
Experience
Id: int
Description: string
Training
Id: int
Description: string
Candidate
Id: int
Name: string
I ended up giving Resume an Id too, but the result is a redundant Review table consisting of just one column, i.e. the resume Id (which equals the application Id).
I have the following model:
public class Student
{
public ObjectId Id { get; set; }
public string Name { get; set; }
}
And the this endpoint in my controller:
public IEnumerable<Student> GetAll()
{
MongoCollection<Student> collection = Db.GetCollection<Student>("students");
List<Student> students = collection.AsQueryable().ToList();
return students;
}
The GetAll() endpoint which is exposed to the public, returns a Student which has an Id of type ObjectId, which is not a standart type. I would rather want it to be of type string.
But if I change it to string, I can't use the .ToList(), getting an error like so:
"Cannot deserialize string from BsonType ObjectId"
I know I can solve this using some projections and conversions, but they will add some ugly and noisy code to the CRUD methods.
What is the best way to keep Id in string type exposed to the public and still be able to use the c# driver's api?
The controller should do the conversion. Generally, controllers should communicate using DTOs with the outside world:
public IEnumerable<StudentReadDTO> GetAll()
{
MongoCollection<Student> collection = Db.GetCollection<Student>("students");
List<Student> students = collection.AsQueryable().ToList();
return students.Select(p => AutoMapper.Mapper.DynamicMap<StudentReadDTO>(p));
}
// this DTO should be used for POST and PUT (i.e where there is no id or the id
// is part of the URL) - after all, it doesn't make sense to send the id from the
// client to the server
public class StudentDTO
{
public string Name { get; set; }
}
// this should be used by reading operations, where the id is read from the server
// inheritance in DTOs is a source of confusion and can be painful, but this trivial
// type of inheritance will be fine
public class StudentReadDTO : StudentDTO
{
public string Id { get; set; }
}
This example uses AutoMapper to do a convention-based mapping between Student and StudentDTO, but you do the mapping yourself if you like.
In the database layer, it makes sense to use ObjectId because of its properties.
Although the suggested answer by #mnemosyn using AutoMapper looks like a great solution and a good pattern to handle those kind of cases, in the case of the MongoDB C# driver you have a dedicated solution and can simply put the following attribute and you are done:
public class Student
{
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
public string Name { get; set; }
}
(I got this solution from the google group of MongoDB)