Skip to content
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

How To Generate Commands with Resolve instances by key #680

Closed
mreizik opened this issue Apr 3, 2019 · 3 comments
Closed

How To Generate Commands with Resolve instances by key #680

mreizik opened this issue Apr 3, 2019 · 3 comments
Labels

Comments

@mreizik
Copy link

mreizik commented Apr 3, 2019

Hi Steven

I'm using your great solidService solution as a base solution for my implementation (WEB API) and the client that need to use my WEB API doesn't want to work with commands as it is complicate his services, meaning he wants to send one request with all fields and I will do the dispatching to the right command .

For example I have 4 kind of customer type in my system :
1.Private customer with Debit Account
2.Private customer with Credit Account
3.Corporate Customer with Debit Account
4.Corporate Customer with Credit Account
the fields are little bit change between them , although there are some filed in common in Base class .
Private customer has first name , last name..
Corporate as has CompanyId , Name..
Debit Account has MinBalance ..
Credit has CreditLimit ..
The Action on those entities are little bit change in my domain (validation , different table mapping )
So I have Commands for each entity with relevant action for example CreatePrivateCustomerWithDebitAcount , UpdatePrivateCustomerWithDebitAcount ...
The client (not part of my source code\team) just wand to send customer request and based on some properties (customerType private\corporate and AcountType Debit\Credit ) i will understated what type of customer is and I will create the right command .
So I decided to copy you Command Infrastructure and call it Request so request will be contract for this client and with RequestDelegatingHandler i will get all the data from the request and send to maybe RequestHandler and based on the data i got from RequestDelegatingHandler I will extract the AcountType and CustomerType and Action iand create Command
something like this :

var CommandName = $"Create{data.CustomerType}CustomerRequest{data.AccountTypes}Command";
than use some how the Resolve instances by key of SimpleInjector
then I will use AutoMapper to map the specif fields from the data to the Instance I got from ResolveByKey .
this way my system still use commands and if i will build anther client for my WEBAPI i can use those command like any other client that want to work with commands instead of one request

So I have difficult in build these infrastructure and will appreciate it you will give me guideline or some example how to implement this and use the Resolve instances by key so I can do this for more future requests from this client for example CreateCard ( split to 2 command create CreditCommand and DebitCommand).
my concern his were to put the RequestHandlerFactory in the solution , where and how to register and how to use I so he will return the right command type fill with the right data.

Anther question he how to expose the project for the Client ? can I generate the Request contract from Swagger so i will have DLL to give my client ?

Thanks!

@dotnetjunkie
Copy link
Collaborator

That is actually a hard-to-answer question. Here are some ideas:

  • First of all, it seems to me that you are providing a task-based service. That your client has internally another respresentation of the domain in mind, shouldn't be your concern. They should be able to do the mapping internally.
  • If the client uses .NET as well, consider providing him with a facade, provided to him as DLL. This facade can simply do the mapping back and forward and do the translation to the generic Web API infrastructure. Even if the client builds a front-end application based on javascript, you can consider supplying them with the appropriate javascript/typescript library that does this mapping.
  • Even if you don't provide the client with a .NET DLL or javascript library, and you expose new operations on a web service, that service will still be a facade on top of your actual API.
  • If you take that approach, consider moving that facade into a separate web service when that API is client-specific. That prevents poluting the main API.
  • I don't see why you would need to do keyed resolves. It is a simple mapping from input messages to internal messages. When an message comes in, you would probably map it to only two or three internal commands. This means you know the command types and their handlers up front. No need to resolve them based on a key. You can either resolve them based on their type, or inject them into the constructor of your facade service. Optionally, you can also use a dispatcher that is able to execute any command by internally forwarding it to the actual command handlers. None of these solutions require to use keys in any way.
  • If you do require a factory, that factory seems specific to this facade. As nothing else in the application requires it, define the factory abstraction close to the facade and its implementation in the facade's web service's Composition Root.

I hope this makes sense

@mreizik
Copy link
Author

mreizik commented Apr 3, 2019

Steven thanks for your replay ,

...That your client has internally another representation of the domain in mind, shouldn't be your concern. They should be able to do the mapping internally."

you are right they have their own domain but they want to avoid breaking compatibility if i change my command and want to avoid the need to compile their code again.

I think that command like Create should be separate for each type of customer as it has different validation and different mapping to EF tables Do you agree ? or it should dispatch the data from one command handler to right domain object?

Of course it may lead this client to send mixed or mismatch fields for the customer (credit data for Debit account for example) but for this situation i throw exception for Invalid Contract. ( I will send Json structure to describe the valid option)

The client is calling me from C# server and the data is for his WinForm app (that only talk with its server and cant talk directly with my API.

By using Facade you mean Adding WEbAPI GateWay above my Rest Services?

I think that what you wrote is what i want:

Optionally, you can also use a dispatcher that is able to execute any command by internally forwarding it to the actual command handlers. None of these solutions require to use keys in any way.

So if I expose WebApI Post action and name it CreateNewCustomer(CustomerData)
and then I see in the properties I got that is is AccountType Debit and customerType Private
i understand I need to create instance of CreatePrivateDebitCustomerCommand.
But I want to avoid switch case even if its just 4 type of customer, this is the reason I wanted to create it by the name = Key and use ResolveByKey.

Any other way to do this ?

Another question his about the internal work of the Commandhandler: Do you have example how to work with AggragateRoot from Commandhandler and not directly with EF context?
(My CreatePrivateDebitCustomerCommand need to create 3-4 Entities and save it to to DB using EF) Where should I put the Transaction scope in the TransactionDecoratorCommandHandler or in the Agg root?

Thanks

@dotnetjunkie
Copy link
Collaborator

you are right they have their own domain but they want to avoid breaking compatibility if i change my command and want to avoid the need to compile their code again.

This is a weird requirement, considering that an externally facing API (a web service) is a strict contract and you can't just introduce breaking changes. That's by definition. This is a promise you should make anyway.

I think that command like Create should be separate for each type of customer as it has different validation and different mapping to EF tables Do you agree ? or it should dispatch the data from one command handler to right domain object?

Hard to answer. I would say it depends, but if these are very different operations with different business logic, triggers, validation, etc, than yes, my primary reaction is: they are different use cases and, therefore, different commands, which each their own command handler.

By using Facade you mean Adding WEbAPI GateWay above my Rest Services?

There seems to be no need to do an extra jump through the web stack. Your original web service is just an interoperability layer on top of your true ICommandHandler<T> layer. Your facade, should forward the call directly to the underlying ICommandHandler<T> infrastructure as well. As I see it, letting your Facade do a http call to the real web service, just gives more overhead, and risk of failure.

But I want to avoid switch case even if its just 4 type of customer, this is the reason I wanted to create it by the name = Key and use ResolveByKey.

Still not sure I see the need for that. You can always inject a Func<AccountType, CustomerType, object> and register that delegate in the container, but in that case you're just moving the logic as in the Composition Root you would still be defining the mapping. This mapping would either be a switch-case or a list of (AccountType, CustomerType) keys with their mapping. Result is identical, where the switch-case is actually more compile-time safe.

Or perhaps you are able to get the command types completely using reflection, based on the values of AccountType and CustomerType, but in that case you would still have custom mapping. Perhaps you could pull this off with AutoMapper, but I'm not sure you need (or really want) all that implicitness and reflection to achieve this. However, if this is what you want, once you have created a command object, you can simply send it to the dispatcher; the dispatcher can simply request the appropriate ICommandHandler<T> implementation based on the type of the supplied command. Still, no keyed registrations for command handlers are needed in that case.

Another question his about the internal work of the Commandhandler: Do you have example how to work with AggragateRoot from Commandhandler and not directly with EF context? (My CreatePrivateDebitCustomerCommand need to create 3-4 Entities and save it to to DB using EF) Where should I put the Transaction scope in the TransactionDecoratorCommandHandler or in the Agg root?

The transaction is a technical concern and should never be done around the aggregate. Besides this is typically much too fine-grained, as there might other things that need to be executed inside the same transaction. The command handler is the ideal boundary on which you would like to apply a transaction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants