0

To explain the problem imagine you have two entities User and Group. The OO implementation has two classes UserModel and GroupModel.

  • UserModel should have a method getGroups() (returning instances of GroupModel) to retrieve all Groups belonging to this particular user. Therefore UserModelmust interact with the GroupModel class.
  • And GroupModel should have a method getUsers()to return all users belonging to this particular group. Therefore GroupModel must interact with the UserModel class.

How do you prevent tight coupling between these classes? I think it's quite common for database relationships to be followed in both direction but I haven't found a good way yet to omit this circular dependency. How do popular ORMs solve this problem?

I would be happy if anyone knows common solutions to the problem.

1 Answer 1

1

I would suggest considering ORM classes as only having responsibility for representing the shape/structure of underlying database records (from tables, but potentially also views/queries/stored procs/etc) rather than trying to include any behaviour in those classes.

Database persistence and Object-Orientation seek radically different goals and therefore require fundamentally different ways of thinking, so instead of thinking about coupling, I'd suggest avoiding the trap of mixing these mindsets together. That is to say, by avoiding including those methods you describe within the ORM classes, concerns around coupling should cease to matter.

The reason loose coupling is preferred in the Object-Orientation mindset is that it allows methods/behaviour to be replaced, overridden or mocked -- this is an important goal for OO because it implies testability, extensibility, flexibility, code reuse, and coherent structure for the behaviour of a program.

For classes which have only data and no methods, coupling generally stops being a useful concern, where the goals of 'OO' are either non-applicable or already satisfied by virtue of lacking any methods.

In the database persistence mindspace, the closest equivalent to coupling would be relationships and constraints between tables, which are typically a good thing because they guard the integrity of the underlying persisted data.

From an application perspective, methods which perform queries would typically exist in separate classes instead; rather than adding methods into the same classes which represent underlying records. This would often be achieved using patterns such as Repository or Data Access Object (DAO), for example:

class UserRepository {
    public function getUsers($groupId) {
        // TODO - query and return list of users for $groupId
    }
}
class GroupRepository {
    public function getGroups($userId) {
        // TODO - query and return list of groups for $userId
    }
}
2
  • > "From an application perspective, methods which perform queries would typically exist in separate classes instead" I was asking this question, because many popular ORMs do not use extra classes to model operations on the underlaying data. And I'm not sure how they achieve that without circular dependencies.
    – Sebi2020
    May 30, 2021 at 13:52
  • @Sebi2020 To clarify, these extra Repository classes are not part of the ORM, but a way of separating the details around usage of the ORM away from application logic; they could also just be "Service" classes or maybe Command/Query classes (depending on your application structure). As far as I'm aware (at least for ORMs that I've used - particularly EntityFramework in .NET, and Hibernate to a lesser extent) circular references between classes are a perfectly fine and normal way of representing table relationships and foreign key constraints. May 31, 2021 at 9:25

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.