2

In my application I have products traveling between stations in a production line. Every pass of the product at a station a result is recorded: success of failure. The relationship between products and stations is many to many.

If I were programming in a procedural language I would have the following function:

get_last_pass_result($station_id, $product_id) {...}

That returns the result of the last time this particular product passed on this particular station.

Now how would you model this logic in OOP terms? I would definitely have class station, and class product. But should I do (php syntax):

$station->get_last_product_pass_result($product_id)

Or

$product->get_last_pass_on_station_result($station_id)

The situation seems symmetric and I wonder what considerations exist do decide between the two (or maybe even some third solution?)

I can't provide here all the existing information about the domain, but feel free to include considerations like: if [an assumption about the domain] then [your design solution], if it feels appropriate

shealtiel
  • 8,020
  • 18
  • 50
  • 82

4 Answers4

3

My take, but based on DDD principles, so I don't know it this suits your needs, but anyway...

So you have a Station, and a Product. I would say that they are both entities that can have references to each other, but the logic you are talking about encompasses these entities and could probably be put in a domain service like ProductPassingService with an operation like GetLastPassFor(product, station).

This domain service would have the responsibility to use the underlying domain entities Station and Product (and repositories to query them) and execute the logic that does not belong either to Station and Product. It keeps the entities Station and Product clean of too much responsibility.

Also, domain entities should not use repositories (DDD - the rule that Entities can't access Repositories directly) so this logic belongs in a domain service.

Community
  • 1
  • 1
L-Four
  • 13,345
  • 9
  • 65
  • 109
  • 1
    I like this answer. Even though it feels like there is a complex idea behind, that I may want or not want to adopt – shealtiel Mar 14 '13 at 08:22
  • 1
    @shealtiel are you aware of DDD? – Zdeslav Vojkovic Mar 14 '13 at 09:21
  • @Zdeslav Vojkovic, no, besides reading its wikipedia page after seeing it mentioned in this answer. I'm a little careful with diving into more and more conceptual rivers, unless they are very central and widely agreed (like MVC) – shealtiel Mar 17 '13 at 12:11
  • 2
    That is actually a very wise approach. I was just asking as entities, repositories and domain services are well defined within discipline of DDD, and are discussed in details in literature (Evans, Nilsson, etc). Honestly, DDD is overkill for most applications, but sometimes it is just what you need – Zdeslav Vojkovic Mar 17 '13 at 14:01
1

It is not completely clear to me whether the Product represents a type of a product (e.g. a chair) or an individual instance of produce (e.g. chair-001, chair-002). From your example it seem like latter is the case, so I will use that, otherwise get_last_pass_result doesn't make much sense.

I believe that I would introduce a Path type (without knowing lot about the domain, though). Now, depending on other use cases, this might be an aggregate root (in DDD lingo) or not.

This means that it would be accessible via Product instance or directly from DB/repository/whatever. With path instance, I can do simply:

var path = product.GetPath(); // if it is accessible only via product
var path = Path.GetPathForProduct(product); // or pathRepository.GetPathFor(), or ...
var result = path.LastResult;

This approach decouples the factory process from the product itself, and enables some other scenarios (e.g. find average duration, etc...)

Zdeslav Vojkovic
  • 14,391
  • 32
  • 45
  • Does this mean that Path is a domain entity that access repositories? – L-Four Mar 14 '13 at 09:10
  • Could be if you decide to follow DDD route. I can't tell whether going DDD is justified in this case, though. However, it will only have a repo if it is an aggregate root, and as I wrote in the answer, I don't know enough about requirements to decide on that – Zdeslav Vojkovic Mar 14 '13 at 09:20
  • Well, actually, domain entities should better not have access to repositories. That's why I proposed a domain service to orchestrate the behavior. (http://stackoverflow.com/questions/5694241/ddd-the-rule-that-entities-cant-access-repositories-directly) – L-Four Mar 14 '13 at 09:24
  • Exactly, that's why I say it would have a repo only if it is an AG, otherwise not. This means that there is a repo to fetch the instances, not that instances know about it. However, if not an AG, then it is accessed via an AG (e.g. `product.GetPath()`). I specifically tried to avoid getting too tied to DDD vocabulary as it is often overkill. That's also why I stay away from proposing services: although it would make sense if DDD approach is chosen, I don't like to prescribe the approach when I have only slight knowledge of the problem – Zdeslav Vojkovic Mar 14 '13 at 09:28
  • @Zdeslav, read your answer carefully, and I don't understand few things. 1) What does path represent? The whole route a product traveled among stations? So it's a list of station basically? 2)How what you propose is different from what L-Three proposed? Is it that L-Three proposed to capture a single pass of a product at a station and you propose to capture the whole path? 3) If so maybe your Path is a list of ProductPassingService instances? (hmm I guess not exactly) – shealtiel Mar 20 '13 at 08:04
1

As always - it depends on how you'd use it.

But there is a nice "how it works" sample on Discovery channel - an automobile factory. During the journey trough conveyor, an automobile receives more and more additional parts. Each automobile has a kind of job schedule attached - a list of jobs to be done in order to complete the task. While it moves through the line, persons responsible for a job make marks about job completion. So when a defect is found - you know the source for sure.

So, going back to a procedural approach. First, it's more natural to use structure+procedure approach instead of pure oop. But it's up to you, of course.

Second - I'd suggest to separate 'product' from a 'production line log' object, which is in one-to-one relationships with a product, but is not probably necessary for it after the product is released. 'Production line log' stores events related to an object processing by stations. Moreover, you can use it as a schedule, i.e. include instructions how a particular product should be processed (as automobiles to include or not certain features like conditioner or fog lights). And 'planned' action should be marked as 'complete' by a worker.

In nowadays terms it can be also expressed in 'event sourcing' terms: during the movement, product modifications are written into a log; so a product can be re-constructed by replaying modification events one-by-one.

mikalai
  • 1,746
  • 13
  • 23
0

I would suggest to put it in the product. My concern is that the number of product is big, but the stations should be fixed, and it would be natural to record specific product's state in the object of that product. For the station, it may only need to record some statistics.

colin chen
  • 26
  • 3
  • The relation between products and stations is many to many, thus it would not be possible to *store the data* about passes on the product. I'm asking where to put the method – shealtiel Mar 14 '13 at 06:50