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?

Mar 13, 2025 - 13:56
 0
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?