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

Strong types vs metadata model #2

Open
markphillips100 opened this issue Aug 25, 2020 · 4 comments
Open

Strong types vs metadata model #2

markphillips100 opened this issue Aug 25, 2020 · 4 comments

Comments

@markphillips100
Copy link

I'm leaning towards thinking a strong type model should be used if documentation is required for response types, i.e. try to use as much of the existing MVC metadata modelling as possible. I especially like the idea that other annotation attributes can be leveraged then; DisplayName etc.

If strong type composition can be achieved by ServiceComposer/ServiceComposer.AspNetCore#155 then great.

Is the intention to support setting a viewmodel dynamic property to a given strong-typed value? Like this:

MyType myObj = new MyType {Name = "Mark"};
vm.MyProp = myObj;

Is it possible to merge lists of strong types too, in cases where a request subscriber wants to add their data to each record?

@mauroservienti
Copy link
Member

In my mind the ideal result would be something like:

[HttpGet("/sample/{id}")]
public Task Handle(HttpRequest request)
{
    var vm = request.GetComposedResponseModel<MyStonglyTypedModel>();
    
    //use the strongly typed model

    return Task.CompletedTask;
}

This could be enabled by injecting something like:

interface IViewModelFactory
{
   object CreateViewModel(HttpRequest request);
}

class MyStonglyTypedModelFactory : IViewModelFactory
{
   [HttpGet("/sample/{id}")]
   public object CreateViewModel(HttpRequest request)
   {
      return new MyStonglyTypedModel();
   }
}

There are many more things that need to be changed to enable the above, as of now the dynamic view model does too many things that need to be moved to a CompositionContext kind of thing to avoid forcing your model to implement an interface and thus violate SRP.

If we can achieve the above another step could be:

//this comes from assembly provided by sales
interface ISalesSampleViewModel
{
   decimal Price{ get; set; }
}

//this comes from assembly provided by marketing
interface IMarketingSampleViewModel
{
   string Description{ get; set; }
}

//this is defined in a branding owned assembly
class MyStonglyTypedModel : ISalesSampleViewModel, IMarketingSampleViewModel
{
   //properties here
}

[HttpGet("/sample/{id}")]
public Task Handle(HttpRequest request)
{
    var vm = request.GetComposedResponseModel<ISalesSampleViewModel>();
    
    //the sales composition handler knows nothing about the marketing side

    return Task.CompletedTask;
}

@mauroservienti
Copy link
Member

@markphillips100 I made (finally) significant progress on this. ServiceComposer/ServiceComposer.AspNetCore#155 Is the first step toward being able to plug-in a completely custom, and strongly typed, view model.

For backward compatibility reasons as of now the only and experimental way to plug-in a custom model is by using interfaces only. This is enabled by a new package I’m working on https://github.com/ServiceComposer/ServiceComposer.AspNetCore.TypedViewModel/pull/2

with the new package installed composition handlers looks like the following:

//this comes from assembly provided by sales
interface ISalesSampleViewModel
{
   decimal Price{ get; set; }
}

//this comes from assembly provided by marketing
interface IMarketingSampleViewModel
{
   string Description{ get; set; }
}

[HttpGet("/sample/{id}")]
[TypedViewModel(typeof(ISalesSampleViewModel))]
public Task Handle(HttpRequest request)
{
    var vm = request.GetComposedResponseModel<ISalesSampleViewModel>();
    
    //the sales composition handler knows nothing about the marketing side

    return Task.CompletedTask;
}

There is no need for a class the implements all the interfaces because that class is dynamically generated at runtime thanks to the TypedViewModel attribute applied to composition handlers.

the same attribute is used by the API generation to provide proper documentation:

screenshot 2020-12-21 061110

you can have a preliminary look at how it works in #3

there is still a lot of work to do before being able to use it in production, I’m confident that this is a promising direction.

@markphillips100
Copy link
Author

I'll take a look as soon as I can @mauroservienti but I really appreciate your efforts here.

My first thoughts are whether this approach would also cater for lists of objects where each object is composed from more than one service, via handler and then subscribers as usual? This is the typical "list of orders" use case where each order has some sales data, some finance data, etc.

@mauroservienti
Copy link
Member

My first thoughts are whether this approach would also cater for lists of objects where each object is composed from more than one service, via handler and then subscribers as usual? This is the typical "list of orders" use case where each order has some sales data, some finance data, etc.

This experiment doesn't support complex object graphs (yet, maybe). It's incredibly complex to support whatever the consumer expects to do, especially if the expectation is "whatever C# allows" 😅

I'm working on 1.8.0 that comes with custom view models factories (implementation merged in ServiceComposer/ServiceComposer.AspNetCore#155). Next step is documentation and a couple more things (https://github.com/ServiceComposer/ServiceComposer.AspNetCore/milestone/1).
Once 1.8.0 is good to go it can be used to provide support for Swagger and friends.

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

No branches or pull requests

2 participants