To keep it simple, let's suppose an application which has Accounts
and Users
. Each account may have any number of users. There's also 3 consumers of UserRepository
:
- An admin interface which may list all users
- Public front-end which may list all users
- An account authenticated API which should only list it's own users
Assuming UserRepository
is something like this:
class UsersRepository extends DatabaseAbstraction {
private function query() {
return $this->database()->select('users.*');
}
public function getAll() {
return $this->query()->exec();
}
// IMPORTANT:
// Tons of other methods for searching, filtering,
// joining of other tables, ordering and such...
}
Keeping in mind the comment above, and the necessity to abstract user querying conditions, How should I handle querying of users filtering by account_id
? I can picture three possible roads:
1. Should I create an AccountUsersRepository
?
class AccountUsersRepository extends UserRepository {
public function __construct(Account $account) {
$this->account = $account;
}
private function query() {
return parent::query()
->where('account_id', '=', $this->account->id);
}
}
This has the advantage of reducing the duplication of UsersRepository
methods, but doesn't quite fit into anything I've read about DDD so far (I'm rookie by the way)
2. Should I put it as a method on AccountsRepository
?
class AccountsRepository extends DatabaseAbstraction {
public function getAccountUsers(Account $account) {
return $this->database()
->select('users.*')
->where('account_id', '=', $account->id)
->exec();
}
}
This requires the duplication of all UserRepository
methods and may need another UserQuery
layer, that implements those querying logic on chainable way.
3. Should I query UserRepository
from within my account entity?
class Account extends Entity {
public function getUsers() {
return UserRepository::findByAccountId($this->id);
}
}
This feels more like an aggregate root for me, but introduces dependency of UserRepository
on Account
entity, which may violate a few principles.
4. Or am I missing the point completely?
Maybe there's an even better solution?
Footnotes: Besides permissions being a Service concern, in my understanding, they shouldn't implement SQL query but leave that to repositories since those may not even be SQL driven.
Best Answer
I would keep all
User
related data-access in theUserRepository
. It is not a good idea to make repositories depend on each other but it's perfectly ok to have a service depend on multiple repositories. So in this case you could have anAccountService
which uses bothUserRepository
andAccountRepository
.The third option you have considered is something I've also used and it has the benefit that now the user list is loaded lazily (although this can be accomplished by other means as well). The huge drawback is that it will be very hard to serialize that object to send it over wire for example and it also couples the data-access layer with your object model.