Should I share types between the web API service and its client? What are the other options?

We are developing a RESTful web API service to provide access to shared data for all our enterprise applications. To help us also publish a client API that encapsulates all the details of the HttpClient and provides strongly typed data access.

Our goal is to start small and gradually add features, while maintaining backward compatibility with already deployed versions of the client API (compatibility with clients of the same major version)

Speaking about design, our team just had a very long discussion about whether types should be shared between the server and the client (for example, through NuGet packages with a version that both the server and the client will depend on), and ultimately with the pros and cons .. .and we cannot decide one way or another.

Sharing types (co-assembly) between client and server

Pros

  • Client model and server model are always updated
  • lack of serialization / deserialization of problems, because the same types are serialized / deserialized
  • no duplication

vs

  • you need to find a way to share types between the server and the client.
  • non-semantic modifications can violate existing client applications (changing the class name or its namespace in the server model), although it does not affect serialized json and therefore should not have any effect.
  • risk of breaking the client without awareness

Separate (but structurally equivalent) types for client and server

Pros

  • the client "model" is less related to the server implementation (just a mirror of Json output on the server, but does not have a relationship of the "same type")
  • server model can be developed without the risk of hacking any client
  • allow you to improve the client model regardless of the server model.
  • the client model is part of the client package, not a “common package” for service between the server and the client.

vs

  • duplication between server code and client code
  • error prone task of maintaining synchronization on the server side and client side.

It seems like a 50/50 preference for every decision on our team.

I personally prefer the second option, because I believe that RESt is a decoupling, and decoupling means that the client does not have to worry about how the server side is implemented (which types, or is this a .NET application anyway), but I wanted to so that we can get rid of possible duplication, perhaps due to code generation or something like that, but could not find any guidance on this

Are there other pros and cons to sharing types between client and server?

If we do not use them, there are ways to reduce maintenance costs when trying to save the client model and server model

+42
source share
3 answers

I would say that if you are not careful, the second option may be less RESTful than the first. REST is less about demutation and more about managing and focusing communication between the client and server.

In a calm system, you know that the connection between the client and the server lies in the definitions of the type of medium and the definitions of link relationships.

In both cases, you efficiently use types between client and server. In the first embodiment, this sharing is explicitly through a specific implementation that can be managed as a nuget package and versioned independently of the client and server.

In the second option, you have two implementations of common types. However, I assume that you are not planning to define a media type that explicitly defines the properties of these types. Therefore, you do not have a single source of truth, you have nothing to determine what data is concluded between the client and the server. How do you know when you’re about to make changes that break the client? At least with a shared library, you can know that the server now uses version 1.4.7 of the common types, and the client uses 1.3.9. You can use semantic versioning in a shared type library to know when you are committing a violation that will cause the client to update.

In the second option, you have a client and a secret that will be independently versioned, and it will be much harder to keep track of whether there are changes between the two versions.

Explanation Media types are always the best way to capture contracts and execute contracts between HTTP clients and servers. However, if you do not want to go there, then the nuget shared library is the best next step, because you isolate the part of the system that is used in conjunction with the client and server implementation. This is one of the key goals of REST. The fact that you are actually using the library for implementing this joint contract only affects users who live on other platforms that cannot use this library.

I coined the term Web Pack few years ago to describe this idea of ​​using the generic nuget package to store shared communications. I have written several articles here and here on this subject.

+22
source

We had a similar discussion - with similar pros and cons - and we took a hybrid approach. We shared the assembly between the client and server, but only the common interfaces. Then we created classes based on client-side interfaces. The advantage was that the actual objects on the client and server can change independently.

+11
source

Domain model classes are mainly defined for server use. On the server side, model types are used by methods defined inside controllers to access data, for example, using entity structures.

But for some reason, you might prefer to give the client a different version of the model object. A well-known approach is to define the classes [DTO] [dto], which are very similar, but not exactly the same as the types of models.

In each method in the controller, when you select data from a database, you need to map the extracted data from the format of their model to a comparable DTO class. [AutoMapper] [automapper] makes this display easier.

Therefore, you need to follow these steps:

  1. Define the types of models within the server project.
  2. Add these packages to your server project as dependencies:
    • AutoMapper
    • AutoMapper.Extensions.Microsoft.DependencyInjection (version 1.2.0)
  3. Define the MappingProfile inside the server project and use services.AddAutoMapper () in the ConfigureServices method in Startup.cs
  4. Change the controller methods to correctly display the received data and return the equivalent DTO as the method output.
  5. In parallel, you can create a new project containing your DTO classes. This project is common for server and client projects.

Then on the client side you do not need to know any details of the types of models. Your client only works with DTO classes. These classes contain all the necessary data on the client side. In some cases, you may need to combine the data of several model objects to provide one container on the client side.

0
source

All Articles