Split single repository/service into pieces in clean architecture?
I'm curious to know your opinion on how to organize code in services and repositories in the context of a three-layer architecture (controllers - services - repositories). Let's say there is a User data model with fields id, first_name, last_name and some other models related to User, for example, Address, Child, Parent and so on. Let's say there is a repository (pseudocode): class UsersRepository: def create_user(...): # can be used in multiple endpoints pass def update_user(...): # can be used in multiple endpoints pass def delete_user(...): # can be used in multiple endpoints pass def add_child(...): pass def find_user_by_id(...): # can be used in multiple endpoints pass def find_users_with_one_child_only(...): # this method is being used in a single endpoint (controller, view) pass def find_users_without_address(...): # this method is being used in a single endpoint (controller, view) pass # another methods And there is a service (pseudocode, so far one for multiple endpoints (views, controllers): class UsersService: users_repository = UsersRepository() def create_user(...): user = users_repository.create_user(...) return user def add_child(...): user = users_repository.find_user_by_id(...) child = users_repository.add_child(user, child) return child def find_users_with_one_child_only(...): # this method is being used in a single controller (view) users = users_repository.find_users_with_one_child_only(...) return users def find_users_without_address(...): # this method is being used in a single controller (view) users = users_repository.find_users_without_address(...) return users And there are 2 controllers (view in the python world): repository = UsersRepository() service = UsersService(repository) POST /users def create_user_controller(...): return service.create_user(...) GET /users-with-one-child-only def users_with_one_child_only_controller(...) return service.find_users_with_one_child_only(...) GET /find-users-without-address def find_users_without_address_controller(...) return service.find_users_without_address(...) You can see that the methods UsersService.find_users_with_one_child_only, Service.find_users_without_address, as well as the methods UsersRepository.find_users_with_one_child_only, UsersRepository.find_users_without_address that they call, are used only once. And in the create_user_controller controller. Of course, the number of such one-time used methods will most likely increase over time. Can this be used? It seems that such a class will turn into a god object (both UsersService and UsersRepository)? If this is not desirable, then how can it be prevented? Do we need to create a separate service and repository for each endpoint? For example: class UsersWithOneChildOnlyRepository: def find_users_with_one_child_only(...): # this method is being used in a single controller (view) pass # of course there can be many another methods class UsersWithOneChildOnlyService: def find_users_with_one_child_only(...): # this method is being used in a single controller (view) users = users_repository.find_users_with_one_child_only(...) return users repository = UsersWithOneChildOnlyRepository() service = UsersWithOneChildOnlyService(repository) GET /users-with-one-child-only def users_with_one_child_only_controller(...) return service.find_users_with_one_child_only(...) The UsersWithOneChildOnlyRepository.find_users_with_one_child_only method is excluded from the UsersRepository repository, and the UsersWithOneChildOnlyService.find_users_with_one_child_only method is excluded from the UsersService service. The same thing happens with the UsersService.find_users_without_address and UsersRepository.find_users_with_one_child_only methods As a result, only crud methods common to many endpoints remain in the UsersRepository repository, while specific one-time methods are moved to their own classes. What do you think about this?

I'm curious to know your opinion on how to organize code in services and repositories in the context of a three-layer architecture (controllers - services - repositories).
Let's say there is a User
data model with fields id
, first_name
, last_name
and some other models related to User, for example,
Address
, Child
, Parent
and so on.
Let's say there is a repository (pseudocode):
class UsersRepository:
def create_user(...):
# can be used in multiple endpoints
pass
def update_user(...):
# can be used in multiple endpoints
pass
def delete_user(...):
# can be used in multiple endpoints
pass
def add_child(...):
pass
def find_user_by_id(...):
# can be used in multiple endpoints
pass
def find_users_with_one_child_only(...):
# this method is being used in a single endpoint (controller, view)
pass
def find_users_without_address(...):
# this method is being used in a single endpoint (controller, view)
pass
# another methods
And there is a service (pseudocode, so far one for multiple endpoints (views, controllers):
class UsersService:
users_repository = UsersRepository()
def create_user(...):
user = users_repository.create_user(...)
return user
def add_child(...):
user = users_repository.find_user_by_id(...)
child = users_repository.add_child(user, child)
return child
def find_users_with_one_child_only(...):
# this method is being used in a single controller (view)
users = users_repository.find_users_with_one_child_only(...)
return users
def find_users_without_address(...):
# this method is being used in a single controller (view)
users = users_repository.find_users_without_address(...)
return users
And there are 2 controllers (view in the python world):
repository = UsersRepository()
service = UsersService(repository)
POST /users
def create_user_controller(...):
return service.create_user(...)
GET /users-with-one-child-only
def users_with_one_child_only_controller(...)
return service.find_users_with_one_child_only(...)
GET /find-users-without-address
def find_users_without_address_controller(...)
return service.find_users_without_address(...)
You can see that the methods UsersService.find_users_with_one_child_only
, Service.find_users_without_address
, as well as the methods UsersRepository.find_users_with_one_child_only
, UsersRepository.find_users_without_address
that they call, are used only once. And in the create_user_controller
controller.
Of course, the number of such one-time used methods will most likely increase over time.
Can this be used? It seems that such a class will turn into a god object (both UsersService and UsersRepository)? If this is not desirable, then how can it be prevented?
Do we need to create a separate service and repository for each endpoint? For example:
class UsersWithOneChildOnlyRepository:
def find_users_with_one_child_only(...):
# this method is being used in a single controller (view)
pass
# of course there can be many another methods
class UsersWithOneChildOnlyService:
def find_users_with_one_child_only(...):
# this method is being used in a single controller (view)
users = users_repository.find_users_with_one_child_only(...)
return users
repository = UsersWithOneChildOnlyRepository()
service = UsersWithOneChildOnlyService(repository)
GET /users-with-one-child-only
def users_with_one_child_only_controller(...)
return service.find_users_with_one_child_only(...)
The UsersWithOneChildOnlyRepository.find_users_with_one_child_only
method is excluded from the UsersRepository
repository,
and the UsersWithOneChildOnlyService.find_users_with_one_child_only
method is excluded from the UsersService
service.
The same thing happens with the UsersService.find_users_without_address
and UsersRepository.find_users_with_one_child_only
methods
As a result, only crud methods common to many endpoints remain in the UsersRepository
repository,
while specific one-time methods are moved to their own classes.
What do you think about this?