10

I'm having serious doubts about the design for my Web application.

I wanted to separate the business logic from the interface so I made a Web API that handles all the requests to the database.

It's an ASP.NET Web API with Entity framework and a unit of work and generic repository pattern. So far, everything is good.

PROBLEM

Where I need help is I can't figure out an efficient way of sharing objects between the API and the application.

I don't want to serialize directly the entity object, I thought it would be a bad practice because if the entity model changes, I could end up with serializing large objects for no reason.

How it's implemented now

Because my interface is ASP.NET Web application in C# and my API is in C#, I made a common library with the definition of all my classes I want to share between them.

I know that solution won't work when I will develop an android app, I will have to create my classes again in Java but that's not my biggest problem.

The problem is I feel like I'm always converting my objects.

EXAMPLE

Here's an example of my work flow :

I start with a model with all the objects and the data annotations for my form then user would POST that model to a controller.

In the controller I have to convert this model to a class in my common library then send that object to my API.

Then a controller in my API catch the call and convert that object to an entity object to update the database.

So I have 3 classes

  1. The model for the view with all the data annotation for the validation (Client)
  2. The common library classes to share the objects (DLL)
  3. The Entity classes (API)

I have a feeling that I do something really wrong. Is there something more elegant? I would like to make sure that I have a good solution for this problem before the project gets too big.

0

1 Answer 1

17

I know it may seem like you are converting objects back and forth all the time between your database objects, your data transfer objects, your client objects with validation logic and so on but I'd say that no, you're not doing anything wrong.

Each of these objects may represent the same unit of information, but they have very different responsibilities. The database object is your communication interface with the database and should be kept in the database layer since it may or may not have different database metadata annotations and/or unnecessary details about the database implementation in it.

Your data transfer object is the communication interface with your API consumers. These should be as clean as possible to facilitate easy consumption from different languages/platforms. This might impose certain restrictions on how these look and behave depending on what API consumers you wish to support.

Your client objects with validation logic is really not a part of your API project, they're part of your consumer project. These cannot be the same as the data transfer objects in this case since you are adding extra client-specific logic (in this case validation attributes) on them which the server knows nothing about (and should not know anything about!) You shouldn't count these objects as part of your API, because they're really not. They're highly consumer application specific and some applications that consume your API might actually not even need to create these object and could just as well survive on just your data transfer objects. For example, if you didn't have any need of validation you wouldn't need an extra layer of objects that are completely identical to your data transfer objects. Generally my strategy here is to evolve the project in stages, I start off by plainly using the data transfer objects as my consumer application objects and when I see the need for application specific handling of data (validation, implementing INotifyPropertyChanged, etc..) I do a quick refactoring and add in the new layer as needed.

To me, it seems like each of the three object types map very nicely to a single responsibility which is clean coding and good practice. Sadly, clean code and good practices sometimes means that you are writing a lot of extra code and jumping through extra hoops "just because". And while coding, it may be hard to appreciate the value that this is giving you - but as soon as you release your application and begin supporting it or adding new features for the next version then you will probably start appreciating that you took the time to separate these concerns properly in the first place. (Not to mention that you'll avoid certain problems that might occur due to improper separation of concerns - and since you have avoided these problems it might be hard to appreciate the mess that you can find yourself in when you have not separated your concerns.)

I also hate writing conversion code between different object types like this, but my solution is usually one of the following:

  • Use a library that does most of the object conversion heavy lifting for you - for example, if you use C# you can use the fantastic AutoMapper library (http://automapper.org/). I believe that there's a couple of other libraries like this, but AutoMapper is the most powerful one I've seen so far.
  • If you can't find a library help you with your object conversions, write a set of utility methods for converting between them. This might suck, but it's worth it in the long run, write the conversion method the first time you need to convert something - don't wait.
5
  • Thank you for your explanations but I still find hard to understand something. I don't understand why the layer for data transfer doesn't have any validation? What if I forget some validations for my next mobile app? At least it wouldn't validate when I call the API instead of doing an exception in my database model. I'm not sure that I understand.
    – Marc
    Commented May 16, 2014 at 14:07
  • 1
    I'm not saying that you shouldn't validate at the API level. To be honest, that's the single most important place to validate at. Validating in your app is just a "nice feature" to help your users to not make mistakes, validating your data transfer objects is for keeping malicious and erroneous data out. Since these are different use cases however, you might need to use different validation frameworks (you will use different validation frameworks if your app and your api isn't written in the same language) and you may validate slightly different things at each level (Cont. in next comment)
    – wasatz
    Commented May 16, 2014 at 16:11
  • 1
    So you should validate your data transfer objects. But you should also make sure that the way you validate them doesn't accidentially introduce any dependencies on any other framework. And of course, as I said earlier, you really cannot be sure that your data transfer objects has been validated at all or that they have been validated by the same framework - so you must "validate twice".
    – wasatz
    Commented May 16, 2014 at 16:14
  • 2
    Mainly, you should try to view your application and your API as two completely different and separate applications. You may be devloping them at the same time, and they might be in the same visual studio solution/eclipse project. But they are really two completely separate programs. When you're working in your application, try to "forget" that you are the one who has created the API and use it just as you would with a normal 3rd party API. That way you'll have a better chance of see how others will feel when using your API and correct the worst parts early on.
    – wasatz
    Commented May 16, 2014 at 16:16
  • 1
    And the same is of course true when working on your API project, try to imagine that you're writing a service that many 3rd party developers are going to use. Try not to think too much about your current application, but rather to keep your mind more on "what services am I providing" and assuming that everyone who is using your API (including yourself) are evil people who are trying to kill your server and make you delete your entire database.
    – wasatz
    Commented May 16, 2014 at 16:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.