Reputation: 660
When we call domain action on Aggregate we pass some primitives/domain_objects we validate some business rules and apply domain event at the end. and when we apply event finally we update the state of the object. At this final steps we have to create objects or load object from a repository. It seems difficult or wrong to me because there is alot of repeatition of tasks.
Here is an example:
def create(uuid:, name:, description:, start_date:, end_date:, customer:, contact_person: , type: , manager:,
department:, author:)
result = Contracts::CreateContract.new(object: self).call(start_date: start_date, end_date: end_date)
raise Exceptions::DomainError.new(payload: result) if result.errors.present?
data = build_creat_event_data(uuid: uuid,
name: name,
description: description,
start_date: start_date,
end_date: end_date,
customer: customer,
contact_person: contact_person,
type: type,
manager: manager,
department: department,
author: author)
apply(ProjectManagementDomain::Events::ProjectCreated.strict(data: data))
end
def apply_project_created(event)
@uuid = event.data[:uuid]
@name = event.data[:name]
@description = event.data[:description]
@status = STATUSES.dig(:draft)
@type = ProjectType.new(event.data[:type])
@start_date = event.data[:start_date]
@end_date = event.data[:end_date]
@customer = Customer.new(event.data[:customer])
@manager = Manager.new(event.data[:manager])
@department = Department.new(event.data[:department])
@contact_person = ContactPerson.new(event.data[:contact_person])
@legacy_id = event.data[:legacy_id]
end
as you can see in above code that I have to build event_data build_creat_event_data
which converts domain objects to primitives for event (because it is recommended to put primitives in event). Then when I have to apply that event I have to create objects again. Am I doing it wrong or is there something missing.
What should be the best approach here?
Upvotes: 1
Views: 144
Reputation: 71
I would start with some thinks in domain to reconsider.
The Contract
created in def create
seems to be another aggregate for me.
If I would define a domain model for that domain I would not create it here, I would just pass contract as an argument to create method. And this would be ProjectManagementDomain::Contract
instead of Contracts::Contract
(this suggest that Contract is a part of another subdomain). BTW the same with customer
, contract_person
, department
... etc).
Another thing is that for me this method & this domain event handled too much data. I would prefer to have more granular domain events with more specific business actions. The good heuristic to apply here is to handle only the data that changes together. Unless you build you project management domain with Jira (*replace Jira with another tool you hate here) integration this could be modelled as set of actions and published as a result domain events. I.e. could you create a project without dates defined? Could you create a project without customer/manager/department/contact_person assigned?
it is recommended to put primitives in event
Yeah. That's right. But you still could pass domain objects here, especially value objects. The only restriction for me would be that they must be serialized to primitive types, and that the objects used here must be in scope of the same bounded context. Also I would never publish objects in domain events that are consumed by some other bounded context (avoid coupling).
So quick TL;DR:
Upvotes: 0