-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Is it possible to have a create and update command that uses the same command type? #10
Comments
This is not so much that Simple Injector is confused, but that your design is ambiguous. These query/handler and command/handler patterns are a form of a more general message based architecture or type based programming. With type based programming it's not the method of a class that describes what should happen, but it is the type. In your case you have a type There are several ways to do this. In its simplest form, just create 2 types: class CreateAddress { public AddressDto Address { get; set; } }
class UpdateAddress { public AddressDto Address { get; set; } }
class CreateAddressHandler : ICommandHandler<CreateAddress> { ... }
class UpdateAddressHandler : ICommandHandler<UpdateAddress> { ... } Notice how this example uses Composition over Inheritance. As a general rule of thumb, don't use inheritance at all. If you think you need it, take a step back, composition is almost always the better solution. The only reason I do inheritance nowadays is when I need to interact with some framework (MVC for instance forces us to inherit from There are options, but in most cases I would go with this. It's the simplest thing to do, and this design has other advantages. For instance, it becomes trivial to give each command its own permission or role: [Permission(Permissions.CreateAddress)]
class CreateAddress { public AddressDto Address { get; set; } }
[Permission(Permissions.UpdateAddress)]
class UpdateAddress { public AddressDto Address { get; set; } } This gives you fine-grained control over every operation in the system. Do note however, that in most cases, you should prevent having a very CRUDdy system. The problem with CRUD is that you are losing a lot of intent. You want to know why some operation is executed, while CRUD only talks about data. This makes it very hard to run more complex business logic behind it different whys. In your case for instance, why exactly is the address updated? There could be a huge difference between correcting a typo in an address, and whether a user actually moved to a different address. It's very likely that quite some different business processes have to be started when a user is moved. In a CRUD system you completely lose that information, which is typically bad. So instead, you might even want to have commands like this: [Permission(Permissions.RegisterCustomer)]
class RegisterCustomer {
public AddressDto { get; set; }
public CustomerInfo { get; set; }
}
[Permission(Permissions.FixCustomerAddressTypo)]
class FixCustomerAddressTypo {
public Guid CustomerId { get; set; }
public AddressDto { get; set; }
}
[Permission(Permissions.MoveCustomer)]
class MoveCustomer {
public Guid CustomerId { get; set; }
public AddressDto { get; set; }
} There are other options though, especially when you stick with CRUD. You can create an envelope to specify whether we're dealing with creates or updates: class Create<TData> { public TData Data { get; set; } }
class Update<TData> { public TData Data { get; set; } } This is especially useful when your handler is generic as well: class CreateHandler<TData> : ICommandHandler<Create<TData>>
{
public void Handle(Create<TData> command) { ... }
}
class UpdateHandler<TData> : ICommandHandler<Update<TData>>
{
public void Handle(Update<TData> command) { ... }
} Does this help? |
This has been extremely helpful I can't believe I didn't think of using composition. |
One thing I've noticed with using composition though is that I may not be using all of the properties of the a DTO class and that could mean I should be making even more use specific DTO's, or am I just being pedantic and that really doesn't matter? |
Can you show an example? |
Sure, but sorry it's now in VB. Here is my command. Public Class CreateProcedureTemplateDetailCommand
Implements ICommand
<Required>
Public Property HeaderId As Integer
<Required>
Public Property Order As Integer
Public Property Detail As ProcedureTemplateDetailDTO
End Class This is a DTO that pretty much copies the entity class in entity framework but is just used by the service layer. Public Class ProcedureTemplateDetailDTO
Public Property Id As Integer
Public Property DisplayValue As String 'For command this is filled
Public Property TypeId As Integer 'For command this is filled
Public Property Type as String
Public Property Reportable As Boolean 'For command this is filled
Public Property Validate As Boolean 'For command this is filled
Public Property Order As Integer
Public Property Questions As List(Of QuestionItemDTO)
Public Property Groupings As List(Of GroupItemDTO)
End Class You can see there is quite a few properties in our ProcedureTemplateDetailDTO that does not need to be filled. Should I just extract them and just put them in the command instead or just leave it as it is? Let me know if more details are needed? |
I could even refactor this more using composition like this. The Db structure would be a header has many stages and each stage is made up of many details. Public Class CreateProcedureTemplateDetailCommand
Implements ICommand
Public Property Header As ProcedureTemplateHeaderDTO
Public Property Stage As ProcedureTemplateStageDTO
Public Property Detail As ProcedureTemplateDetailDTO
End Class
Public Class ProcedureTemplateHeaderDTO
Public Property Id As Integer 'Only This would be used by command
Public Property Name As String
Public Property ProcedureTemplateStages As List(Of ProcedureTemplateStageDTO)
End Class
Public Class ProcedureTemplateStageDTO
Public Property Id As Integer
Public Property Name As String
Public Property Order As Integer
Public Property ConsultantEditable As Boolean
Public Property Details As List(Of ProcedureTemplateDetailDTO)
End Class |
The solution is not that clear cut. In general though I would say:
You can't really say that this is code duplication: Public Class ProcedureTemplateDetailCommandDTO
Public Property DisplayValue As String
Public Property TypeId As Integer
Public Property Reportable As Boolean
Public Property Validate As Boolean
End Class or Public Class UdpateProduceTemplateDetailsCommand
Public Property DisplayValue As String
Public Property TypeId As Integer
Public Property Reportable As Boolean
Public Property Validate As Boolean
End Class If you apply composition, you can also do the following: Public Class UdpateProduceTemplateDetailsCommand
Public Property Details As ProcedureTemplateDetail
End Class
Public Class ProcedureTemplateDetail
Public Property DisplayValue As String
Public Property TypeId As Integer
Public Property Reportable As Boolean
Public Property Validate As Boolean
End Class
Public Class ProcedureTemplateDetailInfo
Public Property Id As Integer
Public Property Type as String
Public Property Order As Integer
Public Property Details As ProcedureTemplateDetail
Public Property Questions As List(Of QuestionItemDTO)
Public Property Groupings As List(Of GroupItemDTO)
End Class
This allows you to use But be careful in overdoing this, it can be beneficial to have a little bit of 'duplication', with commands having their own set of properties. You'll often see that a client that executes a query doesn't use all those properties. When that happens, it really deserves a different DTO. Query results are typically use case specific. Prevent making very generic queries with very generic return types that can be reused all over the place. Instead let your queries focus on a specific use case. This typically makes the overall system simpler and makes it much easier to performance tune the query handlers (and the database) later on. |
Some of the things that I'm taking away from this is I should use DTO's that are used mostly exclusively by queries and commands? Use composition to make up the data that is needed for that specific case. In your case you used the info type but that would contain most of the data but that wouldn't necessarily be useful for every case and should be tuned to specific cases. Here is some refactoring I've done to demonstrate this and these would be used by certain queries. 'These are for generic table columns
Public Class ProcedureTemplateHeaderDTO
Public Property Id As Integer
Public Property Name As String
End Class
Public Class ProcedureTemplateStageDTO
Public Property Id As Integer
Public Property Name As String
Public Property Order As Integer
Public Property ConsultantEditable As Boolean
End Class
'These would be our specfic use case say for certain screens.
Public Class ProcedureTemplateHeaderWithStagesDTO
Public Property Header As ProcedureTemplateHeaderDTO
Public Property Stages As List(Of ProcedureTemplateStageDTO)
End Class
Public Class ProcedureTemplateStageWithDetailsDTO
Public Property Stage As ProcedureTemplateStageDTO
Public Property Details As List(Of ProcedureTemplateDetailDTO)
End Class |
I would even say: mostly exclusively by queries or commands. |
Hi,
I started playing round with the command query pattern and wanted it to work in a crud like manner. So I started by prototyping a create and update commands that would use the same DTO command but this then confuses Simple Injector as it doesn't know which one to instantiate as they use the same command. In my mind it seems that the create and update commands would use the same DTO and the only way to get around this I can think of is to use inheritance for separate commands or to define to separate types with the same properties?
Bellow is some basic code demonstrating what I've done. Starting with the DTO that is used as my commands.
Both of my handlers
The creation of the handler
The text was updated successfully, but these errors were encountered: