{"id":50882366,"url":"https://github.com/beyondnetcode/shell.bootstrapper","last_synced_at":"2026-06-15T14:01:39.755Z","repository":{"id":363164631,"uuid":"1254308280","full_name":"beyondnetcode/Shell.Bootstrapper","owner":"beyondnetcode","description":"Lightweight .NET bootstrapper library for orchestrating application startup with async support, Composite pattern, DI, AutoMapper, and observability adapters.","archived":false,"fork":false,"pushed_at":"2026-06-07T17:08:13.000Z","size":28,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-07T19:08:28.795Z","etag":null,"topics":["aop","bootstrappers","csharp","net"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/beyondnetcode.png","metadata":{"files":{"readme":"README.es.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-30T12:04:14.000Z","updated_at":"2026-06-07T17:08:17.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/beyondnetcode/Shell.Bootstrapper","commit_stats":null,"previous_names":["beyondnetcode/shell.bootstrapper"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/beyondnetcode/Shell.Bootstrapper","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beyondnetcode%2FShell.Bootstrapper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beyondnetcode%2FShell.Bootstrapper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beyondnetcode%2FShell.Bootstrapper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beyondnetcode%2FShell.Bootstrapper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/beyondnetcode","download_url":"https://codeload.github.com/beyondnetcode/Shell.Bootstrapper/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beyondnetcode%2FShell.Bootstrapper/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34365597,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-15T02:00:07.085Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aop","bootstrappers","csharp","net"],"created_at":"2026-06-15T14:01:37.488Z","updated_at":"2026-06-15T14:01:39.748Z","avatar_url":"https://github.com/beyondnetcode.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# BeyondNet.Bootstrapper\n\n[🇬🇧 English](README.md) | [🇪🇸 Español](README.es.md)\n\nUna librería ligera y extensible para orquestar la secuencia de arranque de cualquier aplicación o librería .NET. Basada en el patrón **Composite**, permite encapsular cada paso de inicialización como una unidad independiente y testeable.\n\nConstruida en **.NET 10** con soporte completo para `async/await`, `Nullable Reference Types` y un stack de observabilidad Cloud Native.\n\n---\n\n## Índice\n\n1. [¿Por qué BeyondNet.Bootstrapper?](#por-qué-beyondnetbootstrapper)\n2. [Instalación](#instalación)\n3. [Conceptos Fundamentales](#conceptos-fundamentales)\n4. [Inicio Rápido — Síncrono](#inicio-rápido--síncrono)\n5. [Inicio Rápido — Asíncrono](#inicio-rápido--asíncrono)\n6. [Adaptadores](#adaptadores)\n   - [Dependency Injection](#adaptador-dependency-injection)\n   - [AutoMapper](#adaptador-automapper)\n   - [Observability](#adaptador-observability)\n7. [Ejemplo Real — Combinando Todos los Adaptadores](#ejemplo-real--combinando-todos-los-adaptadores)\n8. [Glosario](#glosario)\n\n---\n\n## ¿Por qué BeyondNet.Bootstrapper?\n\nSin un estándar, el código de arranque tiende a convertirse en un bloque monolítico en `Program.cs` difícil de probar y mantener. Esta librería resuelve ese problema aplicando una sola regla:\n\n\u003e **Cada responsabilidad de inicialización vive en su propia clase. El Composite las ejecuta en orden.**\n\nBeneficios:\n- Cada bootstrapper es unitariamente testeable de forma independiente.\n- La secuencia de arranque es explícita y legible.\n- Agregar o eliminar un paso no modifica el código circundante.\n- El I/O asíncrono en el arranque no puede generar deadlocks.\n\n---\n\n## Instalación\n\nInstala únicamente los paquetes que necesitas:\n\n```bash\n# Core (siempre requerido)\ndotnet add package BeyondNet.Bootstrapper\n\n# Adaptadores oficiales (agregar según necesidad)\ndotnet add package BeyondNet.Bootstrapper.DependencyInjection\ndotnet add package BeyondNet.Bootstrapper.AutoMapper\ndotnet add package BeyondNet.Bootstrapper.Observability\n```\n\n---\n\n## Conceptos Fundamentales\n\n```\nIBootstrapper          ← contrato síncrono\nIBootstrapper\u003cT\u003e       ← contrato síncrono con resultado tipado\nIBootstrapperAsync     ← contrato asíncrono con CancellationToken\nIBootstrapperAsync\u003cT\u003e  ← contrato asíncrono con resultado tipado\n\nCompositeBootstrapper      ← ejecuta la lista de IBootstrapper en orden\nCompositeBootstrapperAsync ← ejecuta la lista de IBootstrapperAsync en orden, respeta cancelación\n```\n\n**Flujo:**\n\n```\nInicio de la app\n  └─ CompositeBootstrapper / CompositeBootstrapperAsync\n        ├─ Paso 1: DatabaseBootstrapper.Run()\n        ├─ Paso 2: AutoMapperBootstrapper.Run()\n        └─ Paso 3: ObservabilityBootstrapper.Run()\n```\n\n---\n\n## Inicio Rápido — Síncrono\n\n### Paso 1 — Implementar `IBootstrapper\u003cT\u003e`\n\n```csharp\nusing BeyondNet.Bootstrapper.Interface;\n\n// Encapsula cualquier lógica de inicialización y expone el resultado en Result\npublic class FeatureFlagBootstrapper : IBootstrapper\u003cbool\u003e\n{\n    public bool? Result { get; private set; }\n\n    public void Run()\n    {\n        // Cualquier configuración síncrona: leer config, validar variables de entorno, etc.\n        Result = true;\n    }\n}\n```\n\n### Paso 2 — Orquestar con `CompositeBootstrapper`\n\n```csharp\nusing BeyondNet.Bootstrapper.Impl;\n\nvar featureFlags = new FeatureFlagBootstrapper();\n\nnew CompositeBootstrapper()\n    .Add(featureFlags)\n    .Run();\n\nif (featureFlags.Result == true)\n    Console.WriteLine(\"Feature flags listos.\");\n```\n\n### Múltiples pasos en secuencia\n\n```csharp\nvar paso1 = new DatabaseBootstrapper(connectionString);\nvar paso2 = new CacheBootstrapper(redisUrl);\nvar paso3 = new FeatureFlagBootstrapper();\n\nnew CompositeBootstrapper()\n    .Add(paso1)\n    .Add(paso2)\n    .Add(paso3)\n    .Run();\n```\n\n---\n\n## Inicio Rápido — Asíncrono\n\nUsa el motor asíncrono cuando un paso requiera I/O (ping a base de datos, llamada HTTP, lectura de archivo).\n\n### Paso 1 — Implementar `IBootstrapperAsync\u003cT\u003e`\n\n```csharp\nusing BeyondNet.Bootstrapper.Interface;\n\npublic class DatabaseConnectionBootstrapper : IBootstrapperAsync\u003cbool\u003e\n{\n    private readonly string _connectionString;\n\n    public DatabaseConnectionBootstrapper(string connectionString)\n        =\u003e _connectionString = connectionString;\n\n    public bool? Result { get; private set; }\n\n    public async Task RunAsync(CancellationToken cancellationToken = default)\n    {\n        // Reemplaza con un ping real a la base de datos\n        await Task.Delay(50, cancellationToken);\n        Result = true;\n    }\n}\n```\n\n### Paso 2 — Orquestar con `CompositeBootstrapperAsync`\n\n```csharp\nusing BeyondNet.Bootstrapper.Impl;\n\nusing var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));\n\nvar dbStep = new DatabaseConnectionBootstrapper(\"Server=localhost;...\");\n\nawait new CompositeBootstrapperAsync()\n    .Add(dbStep)\n    .RunAsync(cts.Token);\n\nif (dbStep.Result == true)\n    Console.WriteLine(\"Conexión a base de datos establecida.\");\n```\n\n\u003e **Regla:** Si algún paso es asíncrono, usa `CompositeBootstrapperAsync` para todos los pasos. No mezcles bootstrappers síncronos y asíncronos.\n\n---\n\n## Adaptadores\n\n### Adaptador Dependency Injection\n\n**Paquete:** `BeyondNet.Bootstrapper.DependencyInjection`\n\nRegistra servicios en `IServiceCollection` y expone la colección poblada como resultado.\n\n**Ejemplo mínimo:**\n\n```csharp\nusing BeyondNet.Bootstrapper.DependencyInjection;\nusing Microsoft.Extensions.DependencyInjection;\n\nvar diBootstrapper = new DependencyInjectionBootstrapper(services =\u003e\n{\n    services.AddSingleton\u003cIGreeter, ConsoleGreeter\u003e();\n});\n\ndiBootstrapper.Run();\n```\n\n**Pipeline completo — registrar, construir provider, resolver:**\n\n```csharp\nusing BeyondNet.Bootstrapper.DependencyInjection;\nusing BeyondNet.Bootstrapper.Impl;\nusing Microsoft.Extensions.DependencyInjection;\n\n// 1. Crear el bootstrapper y registrar dependencias\nvar diBootstrapper = new DependencyInjectionBootstrapper(services =\u003e\n{\n    services.AddSingleton\u003cIOrderRepository, SqlOrderRepository\u003e();\n    services.AddScoped\u003cIOrderService, OrderService\u003e();\n    services.AddLogging();\n});\n\n// 2. Ejecutar el registro\nnew CompositeBootstrapper()\n    .Add(diBootstrapper)\n    .Run();\n\n// 3. Construir IServiceProvider desde la colección poblada\nvar provider = diBootstrapper.Result!.BuildServiceProvider();\n\n// 4. Resolver y usar tus servicios\nvar orderService = provider.GetRequiredService\u003cIOrderService\u003e();\n```\n\n**Pasar una IServiceCollection existente (ej. ASP.NET Core):**\n\n```csharp\n// En Program.cs — inyectar en la colección existente de ASP.NET Core\nvar diBootstrapper = new DependencyInjectionBootstrapper(builder.Services, services =\u003e\n{\n    services.AddSingleton\u003cIPaymentGateway, StripeGateway\u003e();\n});\n\ndiBootstrapper.Run();\n```\n\n---\n\n### Adaptador AutoMapper\n\n**Paquete:** `BeyondNet.Bootstrapper.AutoMapper`\n\nConstruye un `MapperConfiguration` y lo expone como resultado para que puedas crear instancias de `IMapper` en cualquier parte de la aplicación.\n\n**Ejemplo mínimo:**\n\n```csharp\nusing BeyondNet.Bootstrapper.AutoMapper;\n\nvar mapperBootstrapper = new AutoMapperBootstrapper(cfg =\u003e\n{\n    cfg.CreateMap\u003cUserEntity, UserDto\u003e();\n});\n\nmapperBootstrapper.Run();\n```\n\n**Pipeline completo — configurar, construir, mapear:**\n\n```csharp\nusing BeyondNet.Bootstrapper.AutoMapper;\nusing BeyondNet.Bootstrapper.Impl;\nusing AutoMapper;\n\nvar mapperBootstrapper = new AutoMapperBootstrapper(cfg =\u003e\n{\n    cfg.CreateMap\u003cProductEntity, ProductDto\u003e()\n       .ForMember(dest =\u003e dest.FullName,\n                  opt =\u003e opt.MapFrom(src =\u003e $\"{src.Brand} {src.Model}\"));\n\n    cfg.CreateMap\u003cOrderEntity, OrderSummaryDto\u003e();\n});\n\nnew CompositeBootstrapper()\n    .Add(mapperBootstrapper)\n    .Run();\n\n// Construir el mapper — hacerlo una sola vez y almacenarlo (singleton)\nIMapper mapper = mapperBootstrapper.Result!.CreateMapper();\n\n// Usarlo en cualquier parte\nvar dto = mapper.Map\u003cProductDto\u003e(productEntity);\n```\n\n---\n\n### Adaptador Observability\n\n**Paquete:** `BeyondNet.Bootstrapper.Observability`\n\nConfigura el **V1 Observability Stack**: logs estructurados vía `Serilog` (sink OTLP) y trazado distribuido vía `OpenTelemetry`, ambos apuntando a un colector OTLP único.\n\n```\nApp → ObservabilityBootstrapper → Colector OTLP → Tempo (trazas)\n                                                 → Loki  (logs)\n                                                 → Grafana (dashboards)\n```\n\n**Ejemplo mínimo:**\n\n```csharp\nusing BeyondNet.Bootstrapper.Observability;\nusing Microsoft.Extensions.DependencyInjection;\n\nvar services = new ServiceCollection();\n\nnew ObservabilityBootstrapper(services, new ObservabilityConfiguration\n{\n    ServiceName    = \"OrderService\",\n    ServiceVersion = \"2.0.0\",\n    OTLPEndpoint   = \"http://localhost:4317\"\n}).Run();\n```\n\n**Configuración completa con todos los atributos:**\n\n```csharp\nusing BeyondNet.Bootstrapper.Observability;\nusing Microsoft.Extensions.DependencyInjection;\n\nvar config = new ObservabilityConfiguration\n{\n    ServiceName    = \"PaymentService\",\n    ServiceVersion = \"1.4.2\",\n    OTLPEndpoint   = \"http://otel-collector:4317\",\n\n    // Atributos de recurso adicionales enviados en cada traza y log\n    ResourceAttributes = new Dictionary\u003cstring, object\u003e\n    {\n        { \"deployment.environment\", \"production\" },\n        { \"cloud.region\",           \"us-east-1\"  }\n    }\n};\n\nvar obsBootstrapper = new ObservabilityBootstrapper(services, config);\nobsBootstrapper.Run();\n\n// Result expone IServiceCollection con OpenTelemetry registrado\nvar provider = obsBootstrapper.Result!.BuildServiceProvider();\n```\n\n\u003e **Entorno local:** la carpeta `examples/observability/` contiene un `docker-compose.yml` que levanta Colector OTel, Tempo, Loki y Grafana con un solo comando:\n\u003e ```bash\n\u003e cd examples/observability\n\u003e docker-compose up -d\n\u003e ```\n\n---\n\n## Ejemplo Real — Combinando Todos los Adaptadores\n\nEl siguiente muestra un `Program.cs` completo que conecta todo usando un solo `CompositeBootstrapper`.\n\n```csharp\nusing BeyondNet.Bootstrapper.Impl;\nusing BeyondNet.Bootstrapper.DependencyInjection;\nusing BeyondNet.Bootstrapper.AutoMapper;\nusing BeyondNet.Bootstrapper.Observability;\nusing Microsoft.Extensions.DependencyInjection;\n\n// ── 1. Declarar cada bootstrapper ─────────────────────────────────────\nvar services = new ServiceCollection();\n\nvar di = new DependencyInjectionBootstrapper(services, svc =\u003e\n{\n    svc.AddSingleton\u003cIOrderRepository, SqlOrderRepository\u003e();\n    svc.AddScoped\u003cIOrderService, OrderService\u003e();\n});\n\nvar mapper = new AutoMapperBootstrapper(cfg =\u003e\n{\n    cfg.CreateMap\u003cOrderEntity, OrderDto\u003e();\n});\n\nvar observability = new ObservabilityBootstrapper(services, new ObservabilityConfiguration\n{\n    ServiceName    = \"OrderService\",\n    ServiceVersion = \"2.0.0\",\n    OTLPEndpoint   = \"http://localhost:4317\"\n});\n\n// ── 2. Ejecutar todos los pasos en secuencia ──────────────────────────\nnew CompositeBootstrapper()\n    .Add(di)\n    .Add(mapper)\n    .Add(observability)\n    .Run();\n\n// ── 3. Usar los resultados ────────────────────────────────────────────\nvar provider   = di.Result!.BuildServiceProvider();\nvar iMapper    = mapper.Result!.CreateMapper();\nvar orderSvc   = provider.GetRequiredService\u003cIOrderService\u003e();\n```\n\n**Variante asíncrona** (ej. arranque con health-check a base de datos):\n\n```csharp\nusing BeyondNet.Bootstrapper.Impl;\nusing BeyondNet.Bootstrapper.Interface;\n\npublic class DatabasePingBootstrapper : IBootstrapperAsync\u003cbool\u003e\n{\n    public bool? Result { get; private set; }\n    public async Task RunAsync(CancellationToken ct = default)\n    {\n        // Reemplaza con una verificación real de conexión\n        await Task.Delay(20, ct);\n        Result = true;\n    }\n}\n\n// En Program.cs\nusing var cts = new CancellationTokenSource(TimeSpan.FromSeconds(15));\n\nvar dbPing = new DatabasePingBootstrapper();\n\nawait new CompositeBootstrapperAsync()\n    .Add(dbPing)\n    .RunAsync(cts.Token);\n\nif (dbPing.Result != true)\n    throw new InvalidOperationException(\"Base de datos inaccesible al iniciar.\");\n```\n\n---\n\n## Glosario\n\n| Término | Definición |\n|---|---|\n| **Bootstrapper** | Clase que encapsula una responsabilidad de inicio (conexión a DB, registro de DI, perfiles de mapeo, etc.) y expone su resultado en `Result`. |\n| **Composite** | Patrón de diseño estructural que agrupa múltiples bootstrappers y los ejecuta secuencialmente como una unidad. |\n| **IBootstrapperAsync** | Contrato asíncrono. Úsalo cuando un paso realice trabajo I/O-bound (red, disco, base de datos) para no bloquear el thread pool. |\n| **CancellationToken** | Pasado a través de `RunAsync` para permitir que el host cancele toda la secuencia de arranque si se excede un timeout. |\n| **OTLP** | OpenTelemetry Protocol — formato agnóstico de proveedor para enviar métricas, trazas y logs a un colector unificado. |\n| **Colector OTel** | Proxy que recibe señales OTLP y las distribuye a backends de almacenamiento (Tempo para trazas, Loki para logs). |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbeyondnetcode%2Fshell.bootstrapper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbeyondnetcode%2Fshell.bootstrapper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbeyondnetcode%2Fshell.bootstrapper/lists"}