0

I am designing a REST API coupled with a service layer that takes DTOs as input and produces them as output. This works fine for most service calls where the DTO is used to access an underlying library (an embedded OLAP server) that does the work.

There is one service, however, where something seems wrong. The service is responsible for creating, manipulating, and optionally storing objects that represent reports (think excel reports). It does this through the underlying library, which does not expose reports as a resource. Since the reports don't represent a server-side resource unless they are persisted (persistence being my own implementation, not a concept in the library), the service needs to take a report object that holds all the information necessary to construct a report from scratch to be stateless.

I decided that the report service methods should take a ReportDTO, manipulate it, and return the modified version. (This would include operations such as swapping the row and column axes, sorting an axis, and filtering an axis.) My thinking was that since I have all the information available in the ReportDTO necessary to perform all these operations, I might as well perform them on the DTO and return a modified version rather than duplicate the DTO as a domain object. That is not to say the server doesn't interface with any model. Indeed, the server-side code involves validating the report using a third party library (i.e., the validation model is not my own -- i don't have a domain model for the report itself, just DTOs), saving the report if the user so chooses, and executing the report against a database to obtain fresh data. So there is work to be done besides manipulating DTOs, it's just that it's not work tied to a domain model I needed to design myself, but rather one provided to me.

After I implemented the aforementioned operations, I realized something. The operations are business logic operations, are they not? A DTO should not have this. I then had numerous other thoughts: "Okay, that's fine, I'll do the operations in the service layer... Oh, wait. The service layer should only coordinate domain objects that contain business logic, not have business logic itself. Alright, I'll make the ReportDTO a bonafide domain object. Crap, now I am exposing my domain objects from the service layer. This is an API, I should really stick with DTOs as the input and output for the service layer so that changes in my domain model (or changes in the 3rd party domain model I utilize) do not affect the contract with service clients. Fine, I'll have a Report and a ReportDTO.... that have exactly the same members because the ReportDTO already has all the necessary information to manipulate a report (just not execute, validate, or save it) due to the transient report decision I made for the sake of a RESTful API."

Clearly I have countered my every proposed solution. So, what the heck do I do? I don't want to make reports stateful because I really don't see them as a resource until they are actually saved. If I make them stateful I'll need to manage a map or cache of them in my service and deal with multiple clients trying to give their report the same name, implement security to ensure that a user can only see his own reports, etc. All these problems go away with a stateless approach. The side effects, however, are the aforementioned issues. Did I go wrong somewhere? Please help me tackle this design issue before it becomes a permanent part of my API. Thanks.

  • Well I would add caching to your DTO. DTOs are stateless, so always poke the database, unless a cached value exists. Security is not an issue; you can always cache reports by users. – sturcotte06 Jul 28 '14 at 22:50
  • Not sure this is really answerable. You've already made a number of assertions based on the interpreting guidelines as rules. – Dave Hillier Oct 28 '14 at 13:59
1

Who says that a service layer should contain no logic of its own, and instead just delegate to domain objects? That sounds like much too strict a rule to me. it's quite clear that in this case the logic belongs in the service layer, as it does not relate to any object that is persisted as part of the model. Instead it looks to me like you've discovered a utility abstraction, which you may want to consider piling out as a separate service to anything that persists the reports.

0

Don't make the report stateless, make it stateful on your database and business logic layer. I had something similar at work, and my thoughts at that time were:

  1. You should allow quick access to old reports
  2. Let the user queue report requests into the service layer so they can do something else
  3. Break down a report request into smaller DB requests so you can store the results (and requests) in your DB (store per user of course)
  • I do this for stored reports, but what about for reports that are still being constructed? You mean I should have a bunch of temporary, incomplete reports in the database, deleting them if the user decides not to save? What do I do if the user just leaves a temporary report and their session times out? Have a destroy method that cleans out non-persisted reports from the database? It feels to me that the database should not be used as a temporary store. Shouldn't work on temporary, possibly transient objects be considered a 'workflow' and therefore be ideal for stateless processing? – TheBeefMightBeTough Jun 29 '14 at 19:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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