Value Object with identity - domain-driven-design

In an application we have modelled Company as an Entity and Address as a value object:
public class Company : Entity {
public Address PrimaryAddress { get; set; }
public Address SecondaryAddress { get; set; }
}
public class Address : ValueObject {
public string ZipCode { get; private set; } // etc.
public Address(string zipCode) {
ZipCode = zipCode;
}
}
Here, Address does not have an identity and is immutable. To update the PrimaryAddress of a company I replace it with a new Address object.
However, after further discovery into the domain we found that a company may have a variable number of addresses. Representing them as individual properties was no longer feasible. So we refactored Company:
public class Company : Entity {
public Address[] AddressBook { get; set; }
}
We now have a problem of how to update a company's Address. We now care about an address' identity, but only within the context of a Company.
After refactoring we end up with:
public class Company : Entity {
public Address[] AddressBook { get; set; }
public void UpdateAddress(Address newAddress) {
var oldAddress = AddressBook.FirstOrDefault(a => a.Id == newAddress.Id);
if (oldAddress != null)
oldAddress = newAddress;
}
}
public class Address : ValueObject {
public Guid Id { get; private set; }
public string ZipCode { get; private set; } // etc.
public Address(Guid id, string zipCode) {
ZipCode = zipCode;
Id = id;
}
}
// usage
var company = repo.Load<Company>(companyId);
var address = new Address(model.Id, "12345"); // uses id of address we are replacing
company.UpdateAddress(address);
repo.Save(company);
To update an address we locate it using its Id and replace it with a new address object with the same Id.
Therefore is Address still a value object?
Am I right to use the same Id for the updated Address, or by definition (as a value object) should we replace the Id.
If the Address only has identity within the context of the Company entity, should we make this explicit in our model, perhaps generating an Id that is only unique inside the entity (perhaps using the entity's id as part of the calculation)?

Therefore is Address still a value object?
I'd say no, as soon as you give the Address an identity, it ceases being a value object. It means that you stop caring only about its attributes and start giving importance to the Address life cycle in the application, tracking changes in its state, etc.
Am I right to use the same Id for the updated Address, or by
definition (as a value object) should we replace the Id.
I wouldn't reuse an Address Id. From a domain standpoint, when a company moves, it's not the address that changes per se. There will still be a valid street number and buildings at that address. The address defined by that identity still has a reality, and you could even imagine reusing it for some other company. Therefore, when a company moves, it's only the association between Company and Address that needs to change to point to an new Address with a new Id.
If the Address only has identity within the context of the Company
entity, should we make this explicit in our model, perhaps generating
an Id that is only unique inside the entity (perhaps using the
entity's id as part of the calculation)?
Just because you feel the need to single out different addresses inside a Company doesn't mean you have to give them an Id and make them Entities rather than value objects. For instance, if you want to rank addresses inside a Company, define a primary Address, etc. you can perfectly do that with value objects. The address value objects will remain the same, you can just have an indexed collection in the Company, a PrimaryAddress field, and so on.

Related

What are the real world, production grade cases for domain events (understood as events within same bounded context & process)?

If we have a bounded context with lets say 2 aggregates where aggregate1 publishes event1 and aggregate2 wants to react to it, we have 1 ways of doing it:
in process raising event1 > aggregate2 reacting to it
publish event1 to message bus and have some separate process pick it up & invoke aggregate2 method(s)
regardless of being within the same bounded context, if we want to make sure we don't lose event1 (application crashes between aggregate1 is saved, and aggregate2 is saved in reaction to event1, e.g.) i have a hard time finding examples of when would option 1 be better than option 2 (beyond maybe validation)?
i must be missing something but at this point of my knowledge, it seems like a pure theoretical concept to me without some real world value in terms of reliability and ability to maintain correct state.
of course that publishing a message and having separate process listen/react to it might seem like an overkill but is there any practical use of domain events that are not persisted somewhere (even within local DB which gets polled in which case i'd call that a primitive message bus)?
what am i missing?
What is a real world application of domain events within a bounded
context and process?
Requirements:
User can create categories.
Category names must be unique.
User can rename categories.
(Category will have a number of other properties unrelated to naming).
DDD Concepts:
A Category aggregate should be responsible for its own internal invariants, but cannot know the details about other Category aggregates.
How are you going to ensure that the Category Name for the current Category is globally unique without the Category having access to all other categories?
Answer: Domain Events
DomainEvent
public CategoryRenamed : DomainEvent
{
public Category Category { get; }
internal CategoryRenamed(Category category)
{
this.Category = category;
}
}
DomainEventHandler
public CategoryRenamedHandler : IDomainEventHandler<CategoryRenamed>
{
public CategoryRenamedHandler(CategoryRenamed domainEvent)
{
string proposedName = domainEvent.Category.Name;
// query database to ensure that proposedName is not already in use
if (inUse)
throw new Exception($"Name {proposedName} already in use." ;
}
}
Entity
public abstract class Entity
{
List<DomainEvent> _domainEvents = new List<DomainEvent>();
protected AddDomainEvent(DomainEvent domainEvent)
{
_domainEvents.Add(domainEvent);
}
public List<DomainEvent> DomainEvents => _domainEvents;
}
Category
public class Category : Entity
{
public Guid Id { get; private set; }
public string Name { get; private set; }
public Category(Guid id, string name)
{
Id = id;
SetName(name);
}
public Rename(string name)
{
SetName(name);
}
void SetName(string name)
{
// Local Invariants
if (string.IsNullOrWhitespace(name))
throw new Exception("Invalid name");
Name = name;
// Add a domain event for the infrastructure to process
AddDomainEvent(new CategoryRenamed(this));
}
}
Command
public class AddCategoryCommand
{
public Guid Id { get; set; }
public string Name { get; set; }
}
CommandHandler
public class CommandHandler : ICommandHandler<AddCategoryCommand>
{
readonly ICategoryRepository _categoryRepository;
public CommandHandler(ICategoryRepository categoryRepository)
{
_categoryRepository = categoryRepository;
}
public void HandleCommand(AndCategoryCommand command)
{
Category newCategory = new(command.Id, command.Name);
// Check for domain events before committing to repository
DomainEventDispatcher.DispatchEvents(newCategory.DomainEvents);
// Dispatcher will find the CategoryRenamed event and send 'in-process'
// to CategoryRenamedHandler
// If name was is in use an error will be thrown by the handler (see above)
_categoryRepository.Add(newCategory);
}
}
Outcome
Your Category aggregate has enforced its own local invariants and the domain command and domainevent handling infrastructure has been leveraged to ensure uniqueness of name across all categories.

How to initialize sub-collections along with aggregate root in abp.io

What I want to achieve is to figure out where to initialize my sub-collections with aggregate root itself and validate business rules in a best practice way.
Here is my AppService:
public async Task<ReservationDto> CreateReservationAsync(CreateReservationInputDto input)
{
var reservation = await _reservationSystemManager.CreateAsync(
input.ReserverNotes
);
//should i send them directly to manager's createasync method but RequestedItems are dto objects.
//should i iterate through RequestedItems here and send them to manager one by one.
// where to throw business exception if RequestedItems count is 0.
}
Here is my inputdto:
public class CreateReservationInputDto
{
public string ReserverNotes { get; set; }
public Enum.Status Status { get; set; }
public List<CreateReservationItemInputDto> RequestedItems { get; set; }
}
Here is my aggregate root:
public class Reservation : FullAuditedAggregateRoot<Guid>
{
public Enum.Status Status { get; private set; }
public string ReserverNote { get; private set; }
public ICollection<ReservationItem> ReservationItems { get; set; }
public ICollection<OverduePayment> OverduePayments { get; set; }
private Reservation() { }
internal Reservation(
Guid id,
Enum.Status status,
[NotNull] string reserverNote,
) : base(id)
{
ReserverNote = reserverNote;
Status = status;
ReservationItems = new Collection<ReservationItem>();
OverduePayments = new Collection<OverduePayment>();
}
//I could not decide where and how to call this function from Domain Service.
internal void AddReservationItem(ReservationItem reservationItem)
{
if (ReservationItems.Any(r => r.Id == reservationItem.Id))
{
return;
}
ReservationItems.Add(reservationItem);
}
}
Well, depends on your business rules and your use cases. For example, if a reservation must have some reservation items, then I would create it in reservation constructor. Otherwise, if after creating reservation I can add new reservation items then I would be another use case and then AddReservationItem has sense for me.
Generally, if you need to inject more than one service (E.g. IUserRepository and IReservationItemsRepository) for validating your collection or any other property, you can create a domain service and implement your business logic and validate your collection with your needs.
If you don't need to inject any service to implement your business rules you can do it directly in your application service methods. In such cases, you can use data annotations for validating your properties in DTO classes as stated in here.
//should i send them directly to manager's createasync method but RequestedItems are dto objects.
//should i iterate through RequestedItems here and send them to manager one by one.
//where to throw business exception if RequestedItems count is 0.
In these three questions you've asked, should take it separately.
For instance, If you create a domain service class, it could be better to throw an exception if the RequestedItems count is 0 in that class' method. (And you can call, your AddReservationItem method from the domain service's method in that case.)
You can also check the best-practices documents of ABP.

Deleting entity when a referenced entity is deleted

Using DDD, I have 4 aggregate roots where, using the appointment analogy, a clinic can have a number of patients, each patient can have a number of appointments, each appointment can have a number of prescriptions.
Now, to avoid creating a very big bloated Aggregate, Clinic in this case, I have created 4 separate aggregate roots.
public class Clinic
{
public Guid Id { get; private set; }
}
public class Patient
{
public Guid Id { get; private set; }
public Guid ClinicId { get; private set; }
public Patient(Guid clinicId)
{
ClinicId = clinicId;
}
}
public class Appointment
{
public Guid Id { get; private set; }
public Guid PatientId { get; private set; }
public Appointment(Guid patientId)
{
PatientId = patientId;
}
}
Now, the question is how should I manage the scenario where a patient is deleted, in which case all appointments referencing this patient should be deleted too.
I guess this is where a domain expert is going to come in handy. From a technical point of view it probably is going to depend on the architecture you decide on.
100% consistency
Here you could use an application service to first delete the patient and then all the appointments linked to that patient.
Eventual consistency
Using messaging you could publish PatientDeletedEvent that would be responded to by some endpoint that would delete the appointments.
However
You probably do not want to be deleting patients in the first place. Even so, your questions about the appointments for, say, setting a patient Inactive may still results in you wanting to delete future appointments.
This is where you would need a domain expert to guide you in creating the correct model and behaviour.

Entity Framework Linking tables

I’m using Entity Framework 5.0,
Scenario
"Organisation" has a list of "clients" and a list of "Periods" and a "CurrentPeriodID" At the start of each period some or all of the "Clients" are associated with that "Period", this I have done using a link table and this works OK so when I do "Organisation->Period->Clients" I get a list of "Clients" for the "Period".
Next I need to add some objects ("Activities") to the "Clients" for a "Period" so I get "Organisation->Period->Client->Activates" this won’t be the only one there will eventually be several other navigation properties that will need to be added to the "Clients" and the "Activities" and all of them have to be "Period" related, I also will have to be able to do (if possible) "Organisation->Period-Activities".
Question
What would be the best way of implementing the "Activities" for the "Organisation->Period-Client", I Don’t mind what way it is done Code First reverse Engineering etc. Also on the creation of the "Organisation" object could I load a current "Period" object using the "CurrentPeriodID" value which is stored in the "Organisation" object.
Thanks
To me this sounds like you need an additional entity that connects Period, Client and Activity, let's call it ClientActivityInPeriod. This entity - and the corresponding table - would have three foreign keys and three references (and no collections). I would make the primary key of that entity a composition of the three foreign keys because that combination must be unique, I guess. It would look like this (in Code-First style):
public class ClientActivityInPeriod
{
[Key, ForeignKey("Period"), Column(Order = 1)]
public int PeriodId { get; set; }
[Key, ForeignKey("Client"), Column(Order = 2)]
public int ClientId { get; set; }
[Key, ForeignKey("Activity"), Column(Order = 3)]
public int ActivityId { get; set; }
public Period Period { get; set; }
public Client Client { get; set; }
public Activity Activity { get; set; }
}
All three foreign keys are required (because the properties are not nullable).
Period, Client and Activity can have collections refering to this entity (but they don't need to), for example in Period:
public class Period
{
[Key]
public int PeriodId { get; set; }
public ICollection<ClientActivityInPeriod> ClientActivities { get; set; }
}
You can't have navigation properties like a collection of Clients in Period that would contain all clients that have any activities in the given period because it would require to have a foreign key from Client to Period or a many-to-many link table between Client and Period. Foreign key or link table would only be populated if the client has activities in that Period. Neither EF nor database is going to help you with such a business logic. You had to program this and ensure that the relationship is updated correctly if activities are added or removed from the period - which is error prone and a risk for your data consistency.
Instead you would fetch the clients that have activities in a given period 1 by a query, not by a navigation property, for example with:
var clientsWithActivitiesInPeriod1 = context.Periods
.Where(p => p.PeriodId == 1)
.SelectMany(p => p.ClientActivities.Select(ca => ca.Client))
.Distinct()
.ToList();

How update an entity inside Aggregate

I have an aggregate named Campaigns every with a root entity named campaign, this root entity has a list of attempts (entity)
public class Attempts: IEntity<Attempts>
{
private int id;
public AttempNumber AttemptNumber {get;}
//other fields
}
public class Campaign: IEntity<Campaign> //root
{
private int id;
public IList<Attempt> {get;}
//other fields
}
Im using a method to add a campaign attempt
public virtual void AssignAttempts(Attempts att)
{
Validate.NotNull(att, "attemps are required for assignment");
this.attempts.add(att);
}
Problem comes when i try to edit a specific item in attempts list. I get Attempt by AttempNumber and pass it to editAttempt method but i dont know how to set the attempt without deleting whole list and recreate it again
public virtual void EditAttempts(Attempts att)
{
Validate.NotNull(att, "attemps are required for assignment");
}
Any help will be appreciated!
Thanks,
Pedro de la Cruz
First, I think there may be a slight problem with your domain model. It seems to me like 'Campaign' should be an aggregate root entity having a collection of 'Attempt' value objects (or entities). There is no 'Campaigns' aggregate unless you have a parent concept to a campaign which would contain a collection of campaigns. Also, there is no 'Attempts' entity. Instead a collection of 'Attempt' entities or values on the 'Campaign' entity. 'Attempt' may be an entity if it has identity outside of a 'Campaign', otherwise it is a value object. The code could be something like this:
class Campaign {
public string Id { get; set; }
public ICollection<Attempt> Attempts { get; private set; }
public Attempt GetAttempt(string id) {
return this.Attempts.FirstOrDefault(x => x.Number == id);
}
}
class Attempt {
public string Number { get; set; }
public string Attribute1 { get; set; }
}
If you retrieve an Attempt from the Campaign entity and then change some of the properties, you should not have to insert it back into the campaign entity, it is already there. This is how the code would look if you were using NHibernate (similar for other ORMs):
var campaign = this.Session.Get<Campaign>("some-id");
var attempt = campaign.GetAttempt("some-attempt-id");
attempt.Attribute1 = "some new value";
this.Session.Flush(); // will commit changes made to Attempt
You don't need an Edit method. Your code can modify the Attempts in-place, like so:
Attempt toModify = MyRepository.GetAttemptById(id);
toModify.Counter++;
toModify.Location = "Paris";
MyRepository.SaveChanges(); // to actually persist to the DB
Of course how you name the SaveChanges() is up to you, this is the way Entity Framework names its general Save method.

Resources