Skip to content

One Process to Rule Them All. Armature's Universal Dependency Resolution

Ed Pavlov edited this page May 22, 2024 · 1 revision

Leveraging the Build Process for Internal Logic

Armature demonstrates a unique approach to handling the internal "plumbing" of dependency injection. Instead of introducing specialized mechanisms for tasks like constructor selection or argument resolution, it treats these internal operations as regular units within the build process. This means that the same rules and patterns used to build user-defined types are also applied to internal Armature logic.

Unified Build Process

This unified approach simplifies the framework and makes it more flexible. For instance, consider the task of selecting a constructor for a class. In Armature, this isn't handled by a special "constructor selector" but rather by a build action that treats the constructor itself as a unit to be built. This build action can then leverage the full power of build stack patterns and other build actions to determine which constructor is most appropriate based on the current context.

Using Tags for Differentiating Internal Units

To distinguish these internal units from possible-the-same user-defined ones, Armature uses a special type of tag called a ServiceTag. Examples of service tags include:

  • ServiceTag.Constructor: Used to identify units representing constructors.
  • ServiceTag.PropertyCollection: Used for units representing collections of properties.
  • ServiceTag.Argument: Used for units representing arguments for constructors or methods. These tags allow build stack patterns to specifically target and manipulate internal units as needed.

Example: Building an Argument

Let's say Armature needs to resolve an argument for a constructor parameter of type string. This argument is represented as a unit with UnitId(ParameterInfo, ServiceTag.Argument) where the instance ParamterInfo is got by reflection from the corresponding constructor. Armature would then apply its build stack patterns to this unit, just like it would for any other type. For instance, a pattern might match the UnitId and use a CreateByFactoryMethodBuildAction to instantiate a string object.

Benefits of the Unified Approach

This unified approach offers several advantages:

Consistency: The same mechanisms are used for both user-defined and internal units, leading to a more consistent and predictable behavior.

Extensibility: The build process can be easily extended to handle new types of internal units without modifying the core framework.

Flexibility: Developers have full control over how internal units are built and can customize the behavior using the same tools and techniques used for user-defined types.