Using HttpPatch without violating encapsulation - asp.net

I have problem about HttpPatch which violate encapsulation of domain model. I using ASP.NET Core
Let's start with an example: I have a customer domain model which has some attributes and behaviour to imagine it better
public class Customer
{
public Customer(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public string Firstname { get; private set; }
public string Lastname { get; private set; }
public System.DateTime BirthDate { get; private set; }
public void UpdateName(string firstname, string lastname)
{
/*
* Some validations here for example first name first latter should be uppercase and so on..
*/
Firstname = firstname;
Lastname = lastname;
}
public void UpdateBirthDate(System.DateTime BirthDate)
{
/*
* Some validations here for example age must be greater than 18
*/
this.BirthDate = BirthDate;
}
}
As you can see above we have a customer domain model which has private setters and has method which is used to update domain model properties. So to update Firstname and Lastname you gonna call "UpdateName" - method which validates data and after that it will update this model. Also it could throw some domain events if necessary.
Also I'm using Rest architecture in my API. So I have some method where I need to update my customer model partially here is also example:
[HttpPatch]
public async Task<IActionResult> Update([FromBody] JsonPatchDocument<WorkDay> document)
{
var testCustomer = new Customer();
document.ApplyTo(testCustomer);
return Content("Success");
}
So right now let's talk about problem about above code. Let's imagine that testCustomer pulled from storage. After that we update our storage customer by calling ApplyTo. The problem here actually is that it directly sets values to appropriate property which violates encapsulation also the problem in this approach is that it is not calling my methods to throw some domain events or do some validations and so on...
What is workaround of this problem ? Is there any extension which helps me to map those changes to appropriate methods ?

The problem here actually is that it directly sets values to appropriate property which violates encapsulation
The semantics of the HTTP methods are those of a document store. The client has a representation of the resource, it wants the server's representation to match. The entire exchange is fundamentally anemic.
Thus, you end up running into concerns similar to those which encouraged task based user interfaces, which is to say exchanging messages that describe domain semantics, rather than document semantics.
Options:
You can stay with the document semantics, and write code to compute the differences between the server's representation of the document and the client's, translate that difference into domain messages, and apply them to the model.
You can replace the patch document -- instead of using json-patch, you could use your own domain specific patch media type, that describes changes in domain semantics. That makes things easy for the server, but you lose support for general purpose components.
You can change your resource model -- instead of sending changes to the document that is the representation of the domain model, you can send changes to the document that is the representation of the messages being sent to the domain model.
You can use form semantics (POST) to describe client changes, rather than using remote authoring semantics
With information that doesn't belong to you (your model isn't the authority for FirstName or BirthDate -- you are just caching a copy of data provided to you), you might consider separating the model (their data is not the same as our data), or performing the validation on the input (which is to say, walking through the patch-document first, then applying the changes to the model).
But no joke; there are some real tensions when you try to cross from the document domain into your model. See Webber 2011 for more.

Related

Rebus service bus. How to map different types/interfaces

I haven't found a solution how to consume other interface then published.
In simple case if I want to publish IMessage and consume IMessage I have to share assembly with IMessage definition between two applications.
But what if this two applications are developing by different companies.
In this case I have two options:
make an agreement about common interfaces, naming conventions etc and share a common library
let both companies do there job as they are used to do and inside service bus (or application server) map data types.
Second option is more appropriate for me, but I haven't found a solution.
For example, I might have an employee in one system as
public interface IEmployee
{
int ID { get; set; }
string FirstName { get; set; }
string LastName { get; set; }
}
And in other system as
public interface ILightEmployee
{
int id { get; set; }
string full_name { get; set; }
}
I want to publish IEmployee and consume ILightEmployee.
During serialization/deserialization phase in service bus I want to
use some mapping of properties and archive something like this (it is more like a pseudo code):
public class ContractMapper
{
public LightEmployee Map(IEmployee employee)
{
return new LightEmployee()
{
id = employee.ID,
full_name = employee.LastName + " " + employee.FirstName
};
}
}
For example MuleESB provides an editor for this transformations/mapping. LINK
It is unnecessary advanced solution for me, but at least in code I want do to the same thing.
Is it possible using Rebus service bus?
As long as Rebus is able to properly deserialize the incoming JSON object into a concrete class, it will attempt to dispatch the message to all polymorphically compatible handlers.
With the default Newtonsoft JSON.NET and the Jil-based JSON serializer, the rbs2-content-type header will be set to application/json;charset=utf-8, and as long as an incoming message's header starts with application/json and has a compatible encoding, both serializers will try to deserialize to the type specified by the rbs2-msg-type header.
So, if you have a matching concrete class available in the app domain, you can have a handler that implements IHandleMessages<IEmployee> or IHandleMessages<ILightEmployee> - or IHandleMessages<object> for that matter, because that handler is polymorphically compatible with all incoming messages.
The Jil serializer is special though, in that it will deserialize to a dynamic if the cannot find the .NET type that it was supposed to deserialize into.
This means that this handler:
public class HandlesEverything : IHandleMessages<object>
{
public async Task Handle(dynamic message)
{
// .... woohoo!
}
}
coupled with the Jil serializer will be able to handle all messages, dynamically, picking out whichever pieces it is interested in.
I hope the answer gives an impression of some of the possibilities with Rebus. Please tell me more if there's a scenario that you feel is not somehow covered well.

MediaFormatter or ModelBinder for web api PUT method

I have a PUT method in web api which accepts a JSON data and a route data as follows.
[Route("api/v1/Orders/{orderId}/active")]
public HttpResponseMessage Put(Guid? orderId,List<ActiveRequest> activeRequests)
{
}
public class ActiveRequest
{
public int Id { get; set; }
public bool IsActive { get; set; }
}
Now is it possible to simplify the method signature as:
[Route("api/v1/Orders/{orderId}/active")]
public HttpResponseMessage Put(ActiveRequestModel model)
{
}
public class ActiveRequestModel
{
public Guid OrderId { get; set; }
public List<ActiveRequest> ActiveRequests {get; set;}
}
I tried writing a custom ModelBinder by implementing the System.Web.Http.ModelBinding.IModelBinder interface but could'nt find a way to read the JSON data that is coming inside the Request object.
I doubt that is there a way by which I can bind my model with data coming from three different places i.e. from route data, json & form.
You cannot simplify the parameter as described.
Unlike MVC model binding, beacuse of how the Web API formatter works, in Web API you only can have a single parameter that is deserialized from the payload, and a number of simple type parameters coming from route parameters or url query string. The reason is that the creation of the parameter coming from the payload is done in a single pass deserialization of the payload.
So, for your example you need the two parameters in your original version, i.e.:
public HttpResponseMessage Put(Guid? orderId, List<ActiveRequest> activeRequests)
If you want to use the ActiveRequestModel you need to include a payload which has exactly the same structure, so you should include the orderId in the payload, because it will not be recovered from the url (even if the name matches).
Please, read this article which explains how parameter binding works in Web API:
Parameter Binding in ASP.NET Web API
If you read it thoroughly you'll see that you can create and register your own model binder to make it work the same way that an MVC controller, but I think it's not worth the effort (so I include it only in this last paragraph), and it's not the standard way of working.

ViewModel type architecture from Controller to View

I have a fairly complex class of Policies, of which I display a checkbox list of them, the user checks which one they want, and returns back to the server via ajax. The class is fairly complex:
public class Policy {
public int PolicyId { get; set; }
public string PolicyName { get; set; }
... another 15 properties ...
}
To display the list of checkboxes I really only need the Id and Name, so I've created a lightweight class PolicyViewModel that is simply:
public class PolicyViewModel {
public int PolicyId { get; set; }
public string PolicyName { get; set; }
}
So I then pass a List to the View and get a List back containing the selected Policies.
Another developer on my team said that he doesn't necessarily want to translate from the ViewModel to the Policy class on the Ajax call to save the selected policies, but I'm resistant to send a List of policies due to how heavy they are to send to the view, retrieving all the properties, etc.
EDIT: For clarification, on the Ajax save method, to persist to the DB, the call needs a list of the full Policy class.
What is the best way to display this list and get back the values? Is there a better way than I am proposing?
Usually, you wouldn't need a separate model when serializing to json. Simply pluck out what you need from the domain object into an anonymous object.
return policies.Select(x => new { PolicyId = x.PolicyId, Name = x.PolicyName});
on the return trip, you shouldn't have to send anything more than the Ids of the policies that the user selected. Those can be easily mapped back to your policy objects.
public Whatever PostPolicyChoices(IEnumerable<int> ids)
{
var checked = _context.Policies.Where(x => returnIds.Contains(x.PolicyId));
// snip
boom. done.
I will recommend you not to work with Domain objects in your mvc application . You must work just with ViewModels, I think this is best practice for mvc projects. Take a look at Automapper and use it in your project, this will simplify your work, so this should look something like this :
in your [HttpGet] method you will have :
var model =Mapper.Map<IList<Policy>,IList<VmSysPolicy>>(yourlist)
And in your [HttpPost] method you will have :
var domainList=Mapper.Map<IList<VmSysPolicy>,IList<Policy>>(modelList);
And in your mapping configuration you will do :
Mapper.CreateMap<Policy,PolicyVmSysPolicy,>()
.ForMemeber()//Your mapping here
and
Mapper.CreateMap<VmSysPolicy,Policy>()
.ForMemeber//mapping here

superfluous parameters in call to ASMX service

In ASP.NET, WHY its possible to call a function of a web service with more parameter than the function ask (the superfluous parameters are simply unused)?
I need to do a kind of reverse engineering.
I got a web service with the function myFunction(param A, param B)
and I have a web site calling the service with more parameter
MyService.MyFunction(A,B,C);
It's seems working but I don't understand why?
It is not a "web service function". As far as the client is concerned, it's a set of descriptions in a WSDL document. From that description, client code is generated. That client code causes XML (SOAP) to be sent to the service.
It is possible for this to get out of sync. For instance, version 1 of the service may have had 3 parameters on the operation. A client was created for version 1, so it sends 3 parameters.
Version 2 of the service may remove one parameter. If the client is not updated, then it still will send 3 parameters. The service may choose to ignore the extra parameter.
you can also pass a model with the parameters you need, for example, if you are expecting:
firstname, lastname, city
you can make your object as:
public class Person {
public string firstname { get; set; }
public string lastname { get; set; }
public string city { get; set; }
}
and have your method receive a Person object like
public bool SavePerson(Person person) { ... }
later on, you can simply append more properties to the Person object.
So, that might actually be what's going on... let's image that you can pass firstname and lastname, but city is optional.

How does versioning work with Flex remote objects and AMF?

Suppose I use the [RemoteClass] tag to endow a custom Flex class with serialization intelligence.
What happens when I need to change my object (add a new field, remove a field, rename a field, etc)?
Is there a design pattern for handling this in an elegant way?
Your best bet is to do code generation against your backend classes to generation ActionScript counterparts for them. If you generate a base class with all of your object properties and then create a subclass for it which is never modified, you can still add custom code while regenerating only the parts of your class that change. Example:
java:
public class User {
public Long id;
public String firstName;
public String lastName;
}
as3:
public class UserBase {
public var id : Number;
public var firstName : String;
public var lastName : String;
}
[Bindable] [RemoteClass(...)]
public class User extends UserBase {
public function getFullName() : String {
return firstName + " " + lastName;
}
}
Check out the Granite Data Services project for Java -> AS3 code generation.
http://www.graniteds.org
Adding or removing generally works.
You'll get runtime warnings in your trace about properties either being missing or not found, but any data that is transferred and has a place to go will still get there. You need to keep this in mind while developing as not all your fields might have valid data.
Changing types, doesn't work so well and will often result in run time exceptions.
I like to use explicit data transfer objects and not to persist my actual data model that's used throughout the app. Then your translation from DTO->Model can take version differences into account.

Resources