How to use abstracted repositories in dependency injection

A common software design strategy is to explicitly define a class hierarchy, with main classes invoking the specific services they use. This makes the main class dependent on these child classes as the services have to reuse the whole structure to reuse the class and recode any changes. This greatly reduces the usefulness of the classes involved.

Many applications use a web front-end process to invoke deeper transaction processing—a common model for hybrid cloud applications. To do this, administrators must create a web and cloud-side context and bind it to a child process context that runs against the database, often in the data center.

The development of these components includes object classes. Conventional techniques make the structure of the database visible to the parent class supporting the user. Changes in the database structure could create a reverse dependency. This makes the parent class dependent on the child class’s implementation.

dependency injections

The dependency injection process and the repository pattern solve this problem by passing the child classes to the main classes as a service when invoked. Subordinate services are injected into the client class. The client class must not have any explicit child dependencies; Admins need to write the client class to accept the services as injected based only on their visible interface. The client or superclass needs to understand the injected services’ interfaces, but not the implementations, which are opaque.

The traditional direct coupling design approach allows the class representing the user context to directly access the database services and create a reverse dependency. Injecting the service into the user class and context eliminates the inverse dependency, as the following diagram shows.

Direct coupling versus dependency injection.

However, dependency injection is not without problems. It’s important to create both dependent services and parent classes for a common interface, but the two can get out of sync. Some developers also find that dependency injection hinders automatic testing and debugging.

Working with abstracted repositories

Use the Repository design pattern to simplify the management of injected dependencies and the organization of interfaces. Create an abstraction between data access and structure and the business logic of the primary classes and applications. Stored in a repository, this abstraction ensures that the client class and services use the same interface specifications. It enables – but does not guarantee – the decoupling of dependencies.

In a repository abstraction, the repository sits between the client class and the service to define the interface. The injection process injects the interface abstracted from the repository, which also implements the service interface. This process prevents multiple interface definitions from interfering with the exchange of information. However, each injected service requires a repository abstraction. This requirement multiplies the number of repositories where many parent and child classes already exist and requires a dependency inversion.

To fix this, make the abstract repository generic. This step ensures that a repository service can inject the correct service with specific arguments for each of the dependent services it supports, rather than creating a repository for each dependency injection. The created abstraction layer chooses the right service and binds it and all its dependencies to the parent class through the dependency injection process.

The unit of work model

A more difficult problem arises when there is a difference between a transaction from the application user’s perspective and a transaction from the database’s perspective. The superior class and application tends to see the user interaction, but it has to be broken down into database transactions somehow.

A design pattern called a unit of work creates a basket service that connects to the database through repositories. The goal is to represent the entire user transaction as a single entity so that the superclass does not inherit a dependency on the sequence of steps performed in the database, creating another reverse dependency problem.

Dependency injection introduces the child as an injected service that decouples it using the interface provided by the service. An abstract repository creates an intermediate layer of abstraction between the user process class and the database service. The shared abstraction couples the parent and child services.

The unit of work concept means that the abstraction layer creates a single representation class for the sum of database transactions. This provides the context that is coupled to the user through the parent class and the user’s context. The single representation class manages the individual database transactions, so the context of this sequence is also opaque to the user class.

Both abstract repository and unit-of-work techniques are valuable in software testing because the services required by the user-side application are integrated through dependency injection and become part of the unit testing process, rather than requiring separate integration tests. It’s also easier to use an intermediate layer of abstraction to couple test or production data.

The specific tools and procedures used in this advancement depend on the programming language and middleware framework, such as B. Microsoft’s .NET. Examine the application workflow from the online inception context to the database context to decide which model offers the greatest gain in code reuse, functionality, ease of development, and testing.

Comments are closed.