In our API we've got a few central datatypes which need to be "decorated" (so to speak) after retrieval from the database with calculated values. The database is accessed through an ORM which follows a Table/Entity dynamic heavily inspired by the CakePHP 3 database layer, where a Table object is used as an intermediary between the database and the application that takes in and hands out rows as model object instances. So instead of just retrieving data from the database and returning those rows we need to preprocess the returned data before it's actually usable. Here's a few use cases that have come up to better explain what I mean:

  • Objects have numeric values which are translated to user-friendly labels (normally this is logic which I'd keep purely on the client, but for business security reasons some of this data needs to be kept on the server only, admittedly a bit of an edge case)
  • Objects need to have an associated rating value which is pulled from the most recently added rating
  • Based on a combination of calculated values such as this and stored values, a complex schedule object is constructed

On their own, any of these individually are actually pretty easy to do with a simple map() operation over the returned result set. The same thing applies for when you want multiple calculated values, you can just do more map operations to calculate and add those fields as necessary.

That said, this approach has two major drawbacks:

  1. It means that you need to do an additional step of postprocessing everywhere you want to work with these calculated values, which isn't particularly DRY
  2. Some of these transformations depend on other transformations being done first, otherwise they just don't have the data available to work with

To handle both, I've been thinking that the best approach would be to move this code into the ORM, then modify the ORM so that the interface will (externally) allow access to the calculated virtual fields the same way it deals with database columns. Internally it could then map these virtual fields to transformation functions, and internally determine any potential needed dependency transformations to solve the second issue.

(As an aside, I'm wondering if this also removes the need for the returned rows to be actual objects as opposed to simple hashes. Right now each row instantiates a new object with the field data set on it, but if all calculation or modification of the data is moved out of the model then the object just becomes a bag of properties--a hashmap, essentially, with no internal logic of its own. Which may not actually be a bad thing I think)

  • How did this go @moberemk ? – Slamice Mar 22 '16 at 20:06
  • If it's too hard with ORM you may use native SQL query. Usually ORM provide the way of doing safe SQL query because they know well that they can't handle every situation than can be done in raw SQL. – Walfrat Mar 23 '16 at 11:27

You can use repository-like layer for aforementioned cases.

[Repository] Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects.

One repository per case, that uses ORM to read data, enriches them and returns.

So, you would have one unified way to access such instances and hide how those instances are created from outside. Also this would allow you to switch from ORM to raw SQL queries without changing exposed interface.

  • This is what I ended up doing for this project essentially, but I want to note that there were massive performance issues with this over large datasets. Essentially: it works, just not at scale. Accepting for accuracy though. – moberemk Mar 26 '16 at 0:45

I agree with @potfur . Splitting between the "data objects", representing data in the database, and their "business" representation, encapsulating additional logic, calculation, etc. is IMHO the right direction. How the data are represented for given domain/business and what is then technically stored, can be completely different things. Implementing the business logic with objects representing the domain helps adding the value for the customer and makes the communication easier. As for the ORM, you mention scalability issues. I think an ORM is an anti-pattern. Although very useful in smaller/mid- scale, when it comes to scalability, it starts failing. What you could do is to add a caching layer for the "business entities", so you don't have to calculate them every time.

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.