2

Lets focus on a domain object that is related to more other (different!) entities. Sometimes we need one relation and sometimes the other. Let's be agile and say: we don't know in a front how many other relations there will be.

In other words: we have object A that is related one-to-one to B and one-to-many to C and again one-to-one to D and so on. Now, when developing the presentation tier, it often needs a lot of data to display; like table of A with B, or on other place one A with many C and so on.

How we gonna model these relationships?

Here is an example: Competition. Sometimes it comes and needs to be used with e.g. Venue. Sometimes with e.g. List<Event>. Since you care about performances, let's also say that the related objects (Venue and list of Events) come from the single input/repository method/finder method/one SQL join (however you call it) - and that they can be injected into Competition using some mapping tool. This way we express the actual relationship between the entities.

Question: should we keep both relations/references in the Competition class and ignore the other when one is used, or we should make many separate classes - one for each relation? (It doesn't matter if it is 1-1 relation or 1-many, the question is the same.)

In other words, should we have:

[A]

Everything in the single class. Getter methods do represents relationships. It's on us to fill the object if relation is needed.

class Competition {
    ... competition data...

    private Venue venue;
    public Venue getVenue() {
        return venue;
    }

    private List<Event> events;
    public List<Event> getEvents() {
        return events;
    }
}

But then, when your application method return a Competition you don't know what relations are populated and you have to figure that out by looking the application methods code or by method name (e.g. findCompetitionWithEvents...()).

[B]

For each relationship we have a type or interface. One way to do so, without interfaces for the simplicity of this example, is:

class CompetitionVenue extends Competition {
    private Venue venue;
    public Venue getVenue() {
        return venue;
    }
}

and

class CompetitionEvents extends Competition {
    private List<Event> events;
    public List<Event> getEvents() {
        return events;
    }
}

Now the type speaks by itself what relationship is returned and therefore application method that returns it guarantees that relation exists. But then each relationship would have its own class or interface;

EDIT:

[C]

Each relationship is represented using a generic class like Tuple<Competition, List<Event>> and Competition does not have the getter for list of events?

  • 1
    Option B seems like it could spiral out of control really fast. ComptetitoinVenue / CompetitionEvent / CompetitionEventVenue? – hanzolo Mar 27 '14 at 17:27
  • True. Also, CompetitionVenue and VenueCompetitions etc. See edited question with new option C – igor Mar 27 '14 at 17:35
1

I try to follow the "clean code" idea of never returning null (at least as much as I can). That idea suggests that option B (two subclasses) is the better alternative.

I have an actual case that explains this... our system allows you to log a Ticket against various other entities in the system, but only against one type, so you might log it against a Mill or against a Computer. In those cases we have a MillTicket and a ComputerTicket. Only the ComputerTicket has a reference to a Computer. On the SQL side, they're just nullable foreign keys.

An advantage is that a ComputerTicket can have all kinds of fields that are only applicable when I'm logging a Ticket against a Computer.

What this prevents is the ability to have a Ticket against 2 different entities, so that's a design decision with a pretty significant consequence. Also, it means that it's relatively difficult to retroactively change a ComputerTicket into a MillTicket if someone changes their mind. It can be done but it's a pain.

  • Is Ticket related to something else? How do you return Tickets and that other entities that you can join with? – igor Mar 27 '14 at 17:51
  • I asked this as in your case Ticket and ComputerTicket are 'strong' types and they have their purpose for business logic; but what if you need to eg display Tickets with Authors. – igor Mar 28 '14 at 9:16
  • @igor - Ticket has the CreatedByUser property, so you can get from a User to all the Tickets they created. You can also get from a Computer to all the ComputerTickets logged against it. – Scott Whitlock Mar 28 '14 at 11:36
0

Your model is clear example of abstraction, but your are approaching it from wrong way. You want to know location of competition, but you don't know what kind of location it is. So you should create an interface or abstract class that defines this location (lets call it Location) and then derive Venue and Event from it.

Also, you are first talking about domain model and then talking about SQL joins. You are mixing the two things together which is never a good thing. Focus either on one or the second, but never on both at the same time.

  • Venue and Event does not have anything in common, and there is no reason to be of the same (sub)type. Just because we sometimes need one or another that doesn't mean they should be of the same type - and we might need them bought in the same time. Next, sql join here means we have a single place where both objects come. That is a good thing to notice. Will update question. – igor Mar 27 '14 at 17:48
  • @igor They do have something in common: They mean same thing to Competition. And it doesn't have to be subtype. It can be an interface. And where do they come from has absolutely no relevance to how they should be modeled. – Euphoric Mar 27 '14 at 17:52
  • They do not mean the same thing to Competition. It's simply 2 different entities related to Competition. From your last sentence - this means you vote for A? As that approach defines the relationships without knowing how they gonna be used. – igor Mar 27 '14 at 18:22
0

If you only ever need to use either Venue OR Event but never both at the same time, that's pretty much a sum type. I posted an answer a while back showing how to encode them in C#. Unfortunately it's only really practical with lambdas, so unless you're using Java 8, the match method is going to be verbose as hell to use. You could, however, still make use of instanceof to distinguish between the options just as you're already doing in option B. The catch is that using instanceof, if you later add an extra alternative you'll have to find every place you made an if ... else if ... and add the new alternative into the code. Also, this only works for a finite number of possibilities, but that sounds like what you have here.

Using that as a foundation, you could create a class Either<A, B> that contains a value of either type A or type B, where A and B could be Venue and Event. E.g.

public class Competition {
    private Either<Venue, Event> venueOrEvent;
    ...
}

If instead what you need is a finite number of types of Competition (i.e. there's exactly 5 competition types, each with its own set of data) you could just do what you were already doing in option B, except making them inner classes of the Competition class and giving Competition a private constructor so no one can sneak in additional subclasses.

  • It's not OR (ive updated the question). – igor Mar 28 '14 at 9:14

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.