Baubit 2025.23.1

Additional Details

The package has been broken down to smaller projects with granular concerns. Look for Baubit.* packages on nuget.

dotnet add package Baubit --version 2025.23.1
                    
NuGet\Install-Package Baubit -Version 2025.23.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Baubit" Version="2025.23.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Baubit" Version="2025.23.1" />
                    
Directory.Packages.props
<PackageReference Include="Baubit" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Baubit --version 2025.23.1
                    
#r "nuget: Baubit, 2025.23.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Baubit@2025.23.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Baubit&version=2025.23.1
                    
Install as a Cake Addin
#tool nuget:?package=Baubit&version=2025.23.1
                    
Install as a Cake Tool

Baubit

CircleCI NuGet

Introduction

Baubit is a lightweight, modular framework for building scalable and maintainable .NET applications. It provides a clean abstraction for organizing functionality into independently configured modules, supporting recursive loading, dependency injection, and multiple configuration sources.

Why Use Baubit?

  • 🧩 Modular Architecture: Encapsulate related functionality in self-contained units (modules).
  • πŸ—‚οΈ Configuration Management: Modules support their own typed configuration with support for JSON, embedded resources, and secrets.
  • βš™οΈ Clean DI Integration: Each module registers services into IServiceCollection, respecting lifecycle and separation of concerns.
  • πŸ” Recursive Nesting: Modules can declare and load other modules as dependencies.
  • πŸ“¦ Configurable Bootstrapping: Load and configure modules via JSON, appsettings, code, or embedded resources.
  • πŸ§ͺ Testability & Reusability: Modules are isolated and easily testable.
  • πŸ›‘οΈ Validatable: Not only are modules and configurations validated (in isolation) before loding, modules are checked against the module tree to avoid redundant / missing modules.

πŸš€ Getting Started

1️⃣ Installation

dotnet add package Baubit

πŸ“¦ Defining a Module

A Baubit module is a self-contained unit that adds one or more services to the application's IoC container.

public class MyConfiguration : AConfiguration
{
    public string MyStringProperty { get; set; }
}

public class MyModule : AModule<MyConfiguration>
{
    public MyModule(ConfigurationSource configurationSource) : base(configurationSource) { }
    public MyModule(IConfiguration configuration) : base(configuration) { }
    public MyModule(MyConfiguration configuration, 
                    List<AModule> nestedModules, 
                    List<IConstraint> constraints) : base(configuration, nestedModules, constraints) { }

    public override void Load(IServiceCollection services)
    {
        var myStrProp = Configuration.MyStringProperty;
        services.AddSingleton(new MyService(myStrProp));
        //register other services as needed
        base.Load(services);
    }
}

Configuration for each registered service can be passed via the Module's specific configuration

βš™οΈ Loading a Module

Baubit supports multiple ways to load modules:

appsettings.json Example:

{
  "rootModule": [
    "type": "Baubit.DI.RootModule, Baubit",
    "configurationSource": {
      "embeddedJsonResources": [ "MyApp;myConfig.json" ]
    }
  ]
}
Using HostApplicationBuilder
await Host.CreateApplicationBuilder()
          .UseConfiguredServiceProviderFactory()
          .Build()
          .RunAsync();
Using WebApplicationBuilder
var webApp = WebApplication.CreateBuilder()
                           .UseConfiguredServiceProviderFactory()
                           .Build();

// Use HTTPS, HSTS, CORS, Auth and other middleware
// Map endpoints

await webApp.RunAsync();

2. Via ConfigurationSource (Direct Code-Based)

Using HostApplicationBuilder
var configSource = new ConfigurationSource { EmbeddedJsonSources = ["MyApp;myConfig.json"] };
await Host.CreateApplicationBuilder()
          .UseConfiguredServiceProviderFactory(configSource.Build())
          .Build()
          .RunAsync();
Using WebApplicationBuilder
var configSource = new ConfigurationSource { EmbeddedJsonSources = ["MyApp;myConfig.json"] };
var webApp = WebApplication.CreateBuilder()
                           .UseConfiguredServiceProviderFactory(configSource.Build())
                           .Build();

// Use HTTPS, HSTS, CORS, Auth and other middleware
// Map endpoints

await webApp.RunAsync();

3. Manual DI (Without a Host Builder)

var configSource = new ConfigurationSource { EmbeddedJsonSources = ["MyApp;myConfig.json"] };
var services = new ServiceCollection();
services.AddFrom(configSource); // Loads all modules (recursively) defined in myConfig.json
var serviceProvider = services.BuildServiceProvider();

This approach is particularly useful when used in unit tests. See Baubit.xUnit for developing modular unit tests using Baubit

πŸ—‚οΈ Configuration Sources

Baubit supports a mix of external and embedded configuration options:

βœ… Supported Sources
  • jsonUriStrings: Local or remote JSON paths
  • embeddedJsonResources: Embedded resources within assemblies
  • localSecrets: User secrets via GUID ID

1. jsonUriStrings

Loads JSON files from paths accessible to the application.

{
  "modules": [
    {
        "type": "...",
        "configurationSource": {
          "jsonUriStrings": [ "/path/to/myConfig.json" ]
        }
    }
  ]
}

2. embeddedJsonResources

Loads JSON configuration embedded as a resource in a .NET assembly.

{
  "modules": [
    {
        "type": "...",
        "configurationSource": {
          "embeddedJsonResources": [ "MyApp;MyComponent.SubComponent.myConfig.json" ]
        }
    }
  ]
}

3. localSecrets

Loads configuration from secrets.json files using a GUID reference (User Secrets ID).

{
  "modules": [
    {
        "type": "...",
        "configurationSource": {
          "localSecrets": [ "0657aef1-6dc5-48f1-8ae4-172674291be0" ]
        }
    }
  ]
}

This resolves to: <user_secrets_path>/UserSecrets/{ID}/secrets.json

πŸ”— Combining Multiple Sources

You can merge different configuration sources. Example:

{
  "modules": [
    {
        "type": "...",
        "configurationSource": {
          "jsonUriStrings": [ "/path/to/myConfig.json" ],
          "embeddedJsonResources": [ "MyApp;MyComponent.SubComponent.myConfig.json" ],
          "localSecrets": [ "0657aef1-6dc5-48f1-8ae4-172674291be0" ]
        }
    }
  ]
}

All sources are merged in order.

βž• Combining Sources with Explicit Configuration

It’s also valid to define configuration values explicitly alongside configuration sources. The sources are merged with the explicit keys.

{
  "modules": [
    {
        "type": "...",
        "configuration": {
          "myConfigurationProperty": "some value"
        },
        "configurationSource": {
          "jsonUriStrings": [ "/path/to/myConfig.json" ],
          "embeddedJsonResources": [ "MyApp;MyComponent.SubComponent.myConfig.json" ],
          "localSecrets": [ "0657aef1-6dc5-48f1-8ae4-172674291be0" ]
        }
    }
  ]
}

This will result in a configuration that combines values from all three sources plus the inline configuration block.

πŸͺ† Nesting Modules

One of Baubit's most powerful features is its ability to recursively load modules, especially from configuration files. This enables complex service registration trees to be configured externally, promoting reusability and modularity.

πŸ” Nested Configuration Example

{
  "modules": [
    {
        "type": "<module1>",
        "configuration": {
          "<module1ConfigProperty>": "some value",
          "modules": [
            {
                "type": "<module2>",
                "configuration": {
                  "module2ConfigProperty": "some value"
                }
            },
            {
                "type": "<module3>",
                "configuration": {
                  "module3ConfigProperty": "some value",
                  "modules": [
                    {
                        "type": "<module4>",
                        "configuration": {
                          "module4ConfigProperty": "some value"
                        }
                    }
                  ]
                }
            }
          ]
        }
    }
  ]
}

This configuration will load Module 1, along with its nested modules 2, 3, and 4, in a hierarchical manner. Each module can define its own configuration and optionally nest further modules.

πŸ”§ This approach allows dynamic and flexible service registration β€” driven entirely by configuration without changing code.

βœ… Validation

Baubit introduces a powerful validation mechanism to ensure the integrity of your module configurations and their interdependencies.

Configuration Validation

Create your configuration

public class Configuration : AConfiguration
{
    public string MyStringProperty { get; init;}
}

Implement the AValidator class to define validation logic for configuration.

public class MyConfigurationValidator : AValidator<Configuration>
{
    protected override IEnumerable<Expression<Func<Configuration, Result>>> GetRules()
    {
        return [config => Result.OkIf(!string.IsNullOrEmpty(config.MyStringProperty), new Error($"{nameof(config.MyStringProperty)} cannot be null or empty")];
    }
}

Multiple validators can be defined via the validatorKeys configuration property. Validators for modules can also be defined in the similar fashion

{
  "modules": [
    {
        "type": "...",
        "configuration": {
          "validatorKeys": [ "MyLib.MyConfigurationValidator, MyLib" ],
          "moduleValidatorKeys": [ "MyLib.MyModuleValidator, MyLib" ]
          //"myStringProperty" : "" //<not_defined_on_purpose>
        }
    }
  ]
}

Module Validation

While modules can also be validated in isolation (similar to shown above), Baubit allows defining constraints under which modules can/cannot be included in a module tree

public class SingularityConstraint<TModule> : IConstraint
{
    public string ReadableName => string.Empty;

    public Result Check(List<IModule> modules)
    {
        return modules.Count(mod => mod is TModule) == 1 ? Result.Ok() : Result.Fail(string.Empty).AddReasonIfFailed(new SingularityCheckFailed());
    }
}
public class SingularityCheckFailed : AReason
{

}

A module can then simply use these constraints to valide the module tree

public class Module : AModule<Configuration>
{
    public Module(ConfigurationSource configurationSource) : base(configurationSource)
    {
    }

    public Module(IConfiguration configuration) : base(configuration)
    {
    }

    public Module(Configuration configuration, 
                  List<Baubit.DI.AModule> nestedModules, 
                  List<IConstraint> constraints) : base(configuration, nestedModules, constraints)
    {
    }

    protected override IEnumerable<IConstraint> GetKnownConstraints()
    {
        return [new SingularityConstraint<Module>()];
    }
}

This allows preventing redundant service registrations and checking module dependencies at bootstrapping

πŸ“œ Roadmap

Future enhancements for Baubit:

  • βœ… Configuration Extensions: Support for more configuration sources.
  • βœ… Middleware Support: Integrate modules with ASP.NET middleware.
  • 🚧 Logging & Monitoring: Improve logging support within modules.
  • 🚧 Community Contributions: Open-source enhancements and community-driven improvements.

🀝 Contributing

Contributions are welcome! If you’d like to improve Baubit:

  1. Fork the repository.
  2. Create a new branch (feature/new-feature).
  3. Submit a pull request with detailed changes.

For major contributions, open an issue first to discuss the change.

πŸ›  Troubleshooting & FAQs

Q: How do I use multiple modules together?

A: You can initialize multiple modules and inject them into your service container.

Q: Can I override module configurations?

A: Yes! You can extend configurations by passing custom settings to ConfigurationSource.

For more support, open an issue on GitHub.

πŸ”— Resources


Acknowledgments & Inspiration

See ACKNOWLEDGEMENT.md and INSPIRATION.md for details on libraries and ideas that influenced this project.


Copyright (c) Prashant Nagoorkar. See LICENSE for details.

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (3)

Showing the top 3 NuGet packages that depend on Baubit:

Package Downloads
Baubit.Aggregation

Package Description

Baubit.xUnit

Package Description

Baubit.Autofac

Package Description

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2025.23.1 364 6/4/2025 2025.23.1 is deprecated because it is no longer maintained.
2025.18.5 331 5/6/2025 2025.18.5 is deprecated because it is no longer maintained.
2025.15.6 301 4/12/2025 2025.15.6 is deprecated because it is no longer maintained.
2025.14.6 287 4/5/2025 2025.14.6 is deprecated because it is no longer maintained.
2025.13.14 318 3/30/2025 2025.13.14 is deprecated because it is no longer maintained.
2025.13.13 300 3/30/2025 2025.13.13 is deprecated because it is no longer maintained.
2025.13.12 297 3/30/2025 2025.13.12 is deprecated because it is no longer maintained.
2025.13.11 298 3/30/2025 2025.13.11 is deprecated because it is no longer maintained.
2025.13.10 296 3/30/2025 2025.13.10 is deprecated because it is no longer maintained.
2025.13.9 379 3/30/2025 2025.13.9 is deprecated because it is no longer maintained.
2025.13.8 405 3/30/2025 2025.13.8 is deprecated because it is no longer maintained.
2025.13.7 411 3/30/2025 2025.13.7 is deprecated because it is no longer maintained.
2025.11.2 364 3/14/2025 2025.11.2 is deprecated because it is no longer maintained.
2025.10.4 496 3/6/2025 2025.10.4 is deprecated because it is no longer maintained.
2025.10.1 477 3/6/2025 2025.10.1 is deprecated because it is no longer maintained.
2025.7.10 408 2/16/2025 2025.7.10 is deprecated because it is no longer maintained.
2025.7.9 363 2/16/2025 2025.7.9 is deprecated because it is no longer maintained.
2025.7.8 369 2/15/2025 2025.7.8 is deprecated because it is no longer maintained.