7

I have a use case where I display variables in a PDF file. But to get there is not simple, but I could do something like this:

/*
 * takes product model number, retrieves, processes, formats values
 * returns $variables, ready to plug-in to view with no further processing
 */
$variables = $this->repository->getVariables($model);

/*
 * View runs PDF Library code and displays variables and their values in a table
 */
$this->sendToView($variables);

Way 1 - Have Repository Do all the work to transform variables

inside getVariables I would do these:

  1. read raw data from database tables (stored expressions)
  2. run data through expression engine (it evaluates the expressions)
  3. format resulting numeric data (i.e. round to 2 decimal points)
  4. add denominations (i.e. mm or inches)
  5. adorn data (put engineering tolerances, or extra wording around variables)
  6. re-group data for consumption by the PDF API engine (the view)

All the work will be done in getVariables and sendToView will then directly plug the variables as PDF API directs, no transformation needed

Way 2 - Use repository to get raw data only

Opposite extreme is have getVariables do #1 only and have separate classes do their own things .. I could end up with

$expressions = $this->repository->getVariables($model);
$solveExpressions = $this->expressionEngine->process($expressions);
$formattedVars = $this->formatter->format($solvedExpressions);
$denominatedData = $this->denom->addMarkers($formattedVars);
$adornedData = $this->adorner->adorn($denominatedData);
$viewReadyData = $this->viewPreparer->prep($adornedData);
$this->sendToView($viewReadyData);

Or I could mix and match the above and stuff the methods into various classes..

My question

How much of the data transformation, formatting, regrouping, polishing, preparation, etc goes into the repository layer?

(That will help me determine what of the data manipulation will need to be done outside of repository layer, and maybe even what of the data manipulation is to be done in the view layer - i.e. simple regrouping of data for easier way to plug into a table)

P.S. What am I trying to do (data manipulation)

Without focusing on code, in the context of the question, what I am doing is this -- starting with expressions stored in database:

a = 5
b = a + 3

Then I end up with solved expressions:

a = 5
b = 8

Then I do a series or formatting and adorning like so:

8 => 8.00 => 8.00mm => 8.00mm +/-0.01

where during the above is stays in a form similar to

array(
    'a' => '5.00mm +/-0.01',
    'b' => '8.00mm +/-0.01'
);

For the view I transform that into

array(
    0 => array(
        'description' => 'a',
        'value' => '5.00mm +/-0.01'
    ),
    1 => array(
        'description' => 'b',
        'value' => '8.00mm +/-0.01'
    )
);

And I wish to write the above into modern OOP code.

Dennis
  • 8,157
  • 5
  • 36
  • 68
  • Does it make sense to just add an intermediate layer in between? – Teimpz Nov 28 '16 at 17:38
  • If you want to do it OO you need to focus on the "why", not on the "what/how" like above. Start with classes, not functionality. –  Nov 28 '16 at 18:07
  • yes .. but that's pretty arbitrary. I am curious to see if there is a best practice. I was told before that something like "using a Data-Access-Object or a Repository to retrieve raw data from Database" is anemic ... and I might as well *convert* it into an object that's usable by my application without any further transformation. Thus ... it may be good to do data transformation from raw to business object in the repository. But in my case it is less clear what my business object is – Dennis Nov 28 '16 at 18:08
  • @Thomas, I am not sure how to use your approach. . can you give me a starting example? – Dennis Nov 28 '16 at 18:09
  • If you elaborate just a bit on the "why" for doing the above I could try. What is the intention of doing the additions and augmenting the values? –  Nov 28 '16 at 20:00
  • The best practice is the one that most effectively satisfies your business objectives. See also http://meta.stackexchange.com/a/142354 – Robert Harvey Nov 28 '16 at 20:44
  • @Thomas ... why? The user of the application requested it. Why user requested that way? the customer needs to see if the value is in mm or inches, as customer's country is used to a particular measurement system. Why tolerances? Customer needs to see tolerances as well so that the manufactured equipment fits for the parts that are designed for it. Neither though tells me anything about how to code that, not from what I can tell. Why am I coding it this way? I'm refactoring the existing legacy code, and it currently does not properly fit the purpose -- too many bugs, so I'm rewriting it – Dennis Nov 28 '16 at 21:19
  • 1
    A user requesting it is a requirement. The "why" is in the business/use case. You need to model towards this, not against a single requirement. Basically the answer from kayess is fine. But the whole story is more complex. You should model the use cases first, before starting to model classes. –  Nov 28 '16 at 22:02
  • I would be cautious regarding how much "cosmetization" the repository layer adds on the data it returns. I think a great rule of thumb is: **only do reversible customizations**. Why ? Because there (or will) be other services in the future that will rely on that repository. In your case: the "export-to-PDF" functionality will soon be side-by-side with a "export-to-CSV" or something similar. – Radu Murzea Dec 23 '16 at 12:40

1 Answers1

3

Based on your comments, I would highly recommend you to not get fooled by so called best practices and other wizardry. Primarily you should choose a way to implement your solution in a way that helps:

  • your stakeholders/boss to reach business goal using your solution - the code in place.
  • you and your team to be able to fastly respond to the arisen problem/task, in a way that everyone understands

You also write about anemia and business object. Please prove me wrong but I fail to percieve any behaviour or business logic in the set of method calls. They just seem to doing a series of data transformation, without any validation or so. I have a gut feeling that it's used for some sort of reporting, but the true intention is not really clear.

The only useful thing I could help you with is, to examine your code, capture the technical areas and split them up to modules those have explicit boundaries. The Separation of concerns design principle can help here drawing the boundaries:

  • Repository: held responsible for reading and writing the data in your data store
  • Expression engine: evaluating the data
  • Formatter: formatting and adorning
  • Output to viewing: convert/represent the above to a given output form

Each of these modules should be responsible for their own set of concerns. Create classes with respective methods that does what are respectively responsible for and nothing more. Whenever you encapsulate these concerns into set of modules you are also increasing cohesion and may even help the testability of your code.

kayess
  • 241
  • 4
  • 12
  • user selects a product, values for product are created and are written to the database for a particular order. After that, every time the order is pulled up in the system, those values are displayed. It's not reporting really but... viewing an order. Use case may be called "Display an order". part of that use case is "display values for that product". In a way all of the above is just a complicated way to show values as part of that use case – Dennis Nov 28 '16 at 21:30
  • Well, if you have a clearly defined use case it's good as it reflects a business use case. You could although implement it in a way that clearly communicates the business use case that could utilise commands and queries. The "display values for that product" is definitely a query, which you could decorate with formatting, etc. – kayess Nov 29 '16 at 09:09