Frequency Asked Questions
Frequency asked questions and corresponding responses are included here.
General
Why I have to use comby.AddDomainEventHandler instead of agg.AddDomainEventHandler?
The limitation is technical: in Golang, it is not possible to define generic methods for a non-generic struct. For more details, refer to https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md#methods-may-not-take-additional-type-arguments.
To define aggregates with strongly-typed Domain Event Handlers and avoid boilerplate code, we utilize the helper functions provided by comby directly. This approach aims to simplify the codebase for developers.
// instead of: agg.AddDomainEventHandler
// we use: comby.AddDomainEventHandlerThe comby.AddDomainEventHandler function handles both type assertions and the registration of Domain Event Handlers within the underlying Aggregate. By leveraging this method:
- Boilerplate code is minimized.
- Code readability and maintainability are improved.
- Runtime errors due to incorrect type assertions are avoided.
For registering components relevant to the Facade, you can use the following functions:
- RegisterAggregate
- RegisterEventHandler
- RegisterCommandHandler
- RegisterQueryHandler
Within your domain, you can use the following functions to register your domain-specific handlers or to apply domain events:
- ApplyDomainEvent
- AddDomainCommandHandler
- AddDomainQueryHandler
- AddDomainEventHandler
Why is my Command serialized before it is sent to the CommandBus?
Serialization is performed upfront for pragmatic and preventive reasons. In certain situations, serialization can fail unexpectedly, especially with specific data types, leading to errors. If serialization issues were only encountered at the broker stage, it could delay problem detection and complicate debugging.
By serializing commands (and queries) early, any serialization issues can be identified and communicated promptly during development, ensuring that developers can address the problem before it reaches later stages of the system.
Additionally, the performance impact of early serialization is negligible when compared to the overhead of I/O or storage operations, making this approach a practical trade-off for better error handling and robustness.
Best Practices
What is the best way to represent a linear process?
A linear process can be effectively represented by a combination of commands and queries. Especially when instantiating new aggregates, it is useful to go through a specific process before using them. We use the term "provisioning" for this. Provisioning is essentially nothing more than a controlled sequence of commands and queries until the final result of this process is achieved. An example of this is the provisioning of a new tenant—new groups must be created, permissions distributed, etc.
To achieve this, we typically create a new domain provision and add 1 to 2 commands and queries. We also ensure that only authorized groups have the permissions to perform the provisioning. The commands are then exposed via a REST API and can be triggered externally. Internally, the command handler can then carry out the individual steps of the provisioning, such as creating new groups, distributing permissions, etc.
Projection
How to sort by custom fields?
By default, projections support sorting by standard fields. However, if you need to sort by custom fields in your aggregate models, you can implement the ModelListFieldComparer interface.
The ModelListFieldComparer interface was introduced in projection.model.go and allows aggregates to define which custom fields can be used for sorting. To enable custom field sorting, implement the GetModelFieldComparable method on your aggregate:
func (m *Report) GetModelFieldComparable(orderBy string) (any, bool) {
field := strings.TrimPrefix(orderBy, "-")
switch field {
case "company_ticker":
return m.CompanyTicker, true
default:
return nil, false
}
}This method receives the orderBy parameter and should:
- Extract the field name (removing the
-prefix for descending order) - Return the field value and
trueif the field is supported for sorting - Return
nilandfalseif the field is not supported
Once implemented, the projection system will use this method to enable sorting by your custom fields in list queries.