Skip to content

Dependency Injection for Nested Objects in a File Extraction System #236

Closed

Description

@arthurbrenno

Hi, maintainers.

Context

I'm working on an API that involves file extraction. The system architecture includes several layers:

  1. FileExtractionController
  2. FileExtractionService
  3. FileExtractor (an internal engine)

Current Implementation

The FileExtractor class is instantiated within the FileExtractionService without dependency injection. However, FileExtractor requires a repository implementation to be passed in its constructor.

Problem Statement

I want to inject dependencies that my API uses indirectly, specifically for the FileExtractor class. The challenge is to avoid passing these dependencies through each layer (Controller -> Service -> FileExtractor).

Desired Solution

Implement a mechanism to auto-inject the required dependency (repository) into the FileExtractor object without explicitly passing it through intermediary layers.

Current Code Structure

class FileExtractor(CPrototype):
    """
    Abstract class for extracting content from files.
    This should be used as a base class for specific file extractors.
    """

    file: Annotated[
        UploadFile, Meta(title="File", description="The file to extract content from")
    ]

    repository: Annotated[
        Optional[FileExtAPIKeyRepository],
        Meta(
            title="API Key Repository",
            description="Repository for storing API keys that may be used by external services for the file extraction process",
        ),
    ] # Note: I will move this to subclasses that needs a repository, placed here because it's easy to understand

    @classmethod
    def from_file(cls, file: UploadFile) -> FileExtractor:
        """Factory method to create a FileExtractor from a file."""
        return cls(file=file)

    async def extract_contents(self) -> ExtractedFile:
        """Extracts content from the file."""
        raise NotImplementedError("This method should be implemented by subclasses.")

Additional Notes

  • The FileExtractor class is not directly related to the API; it's an internal component for file content extraction.
  • The API needs to pass certain dependencies to this class.
  • I'm uncertain about the correct placement of the @inject decorator (class level or method level).

Questions

  1. How can I implement dependency injection for the repository in the FileExtractor class without modifying the entire object creation chain?
  2. Are there best practices or design patterns for handling dependency injection in deeply nested object structures?
  3. What are the implications of using dependency injection at different levels (class vs. method) in this context?

Any insights or code examples demonstrating a clean, maintainable solution would be greatly appreciated. I didn't find this kind of thing in the documentation, only things I've found is an @Inject decorator, but all of these decorators are located in the integrations module.. Do I have to call container.get everywhere I need it? Having to import it everywhere I need it

Activity

  1. Tishka17 commented on Aug 30, 2024

    @Tishka17
    Member

    As for now, I can suggest reading this: https://dishka.readthedocs.io/en/stable/di_intro.html

    I'llt think about your question later and maybe add more details

  2. arthurbrenno commented on Aug 30, 2024

    @arthurbrenno
    Author

    Okay. Thanks.

  3. Tishka17 commented on Sep 3, 2024

    @Tishka17
    Member
    1. I see that FileExtractor here depends on the file it is called with. So, probably, have an Abstract Factory is a good idea. I do not see you logic of creating extractors, so I suppose that you might want to have several of them in one service. It would be something like
    class FileExtractorFactory:
        def new_file_extractor(self, file: File) -> FileExtractor
    
    1. That FileExtractorFactory implementation will know how to create concrete FileExtractor, passing all necessary objects like repositories. So, they will be its' dependencies.

    2. So, you have classes like FileExtractionService, FileExtractorFactory, Repositry and maybe some others. They have clear lifespan like REQUEST-scope, so all of them can be moved to dishka container.

  4. locked and limited conversation to collaborators on Sep 3, 2024
  5. converted this issue into a discussion #239 on Sep 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions