Version: 1.0
Fecha: 16 de Marzo, 2026
Estado: NORMATIVO - Guia de Referencia para Desarrollo
Arquitecto: Carlos Alberto Torres Camargo
Servicio de referencia: nebula-accounting-core (Centro de Costos)
Este documento describe la anatomia completa de un microservicio Nebula construido sobre el stack Simappe. Utiliza nebula-accounting-core como servicio modelo para que cualquier desarrollador pueda:
Audiencia: Desarrolladores Backend (Senior, Semi-Senior y Junior) del equipo Centrica.
Este documento complementa la Guia Crear Feature Backend con una vision desde el codigo existente.
com.centrica.nebula.accounting.core/
|
+-- NebulaAccountingCoreApplication.java # Bootstrap Spring Boot
+-- ServletInitializer.java # Soporte WAR deployment
|
+-- config/ # Configuraciones Spring
| AppConfig.java
| AsyncConfig.java
| ClientConfig.java
| DockerMongoHealthConfig.java
| KafkaAdapterConfig.java
| MongoConfiguration.java
| MongoRepositoryConfig.java
| MultiTenantJpaConfig.java
| ResourceLockConfiguration.java
|
+-- constants/ # Constantes de dominio
| AccountingDomainConstants.java
|
+-- monitoring/ # Metricas Prometheus
| ApplicationMetrics.java
|
+-- util/ # Utilidades
| AccountingMessages.java
|
+-- v1/ # Versionado de API
+-- cdc/ # Dominio: Centro de Costos
| +-- controller/ # Capa HTTP
| | CentroCostosController.java
| | TipoCentroCostosController.java
| |
| +-- service/ # Capa Orquestacion
| | CentroCostosService.java
| | TipoCentroCostosService.java
| |
| +-- component/ # Capa Logica de Negocio
| | CentroCostosComponent.java
| | TipoCentroCostosComponent.java
| |
| +-- entity/ # Capa Persistencia (JPA)
| | CentroCostosEntity.java
| | TipoCentroCostosEntity.java
| |
| +-- repository/ # Capa Acceso a Datos
| | CentroCostosRepository.java
| | TipoCentroCostosRepository.java
| |
| +-- lightloader/ # Optimizacion N+1
| CentroCostosLightLoader.java
|
+-- kafka/ # Eventos asincronos
AuditLogListener.java
AuditLogRepository.java
| Nivel | Convencion | Ejemplo | Regla |
|---|---|---|---|
| Raiz | com.centrica.nebula.{modulo}.core |
com.centrica.nebula.accounting.core |
Un modulo por microservicio |
| Version API | v{n} |
v1 |
Permite coexistencia de versiones |
| Dominio | Acronimo o nombre corto | cdc (Centro de Costos) |
Agrupa feature completa |
| Capa | Nombre de responsabilidad | controller, service, component, entity, repository |
Patron 5 capas obligatorio |
Documento normativo: Estructura de Proyecto y Paquetes y Reglas de Desarrollo de Servicios
@SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class,
MongoAutoConfiguration.class,
HibernateJpaAutoConfiguration.class
})
@EnableDiscoveryClient
@EnableFeignClients(clients = { SimappeAdminClient.class })
@Import({ MultiTenantJpaConfig.class, MongoConfiguration.class })
@ComponentScan(excludeFilters = { /* excluye beans multi-tenant de Simappe */ })
@EntityScan("com.centrica.nebula.accounting.core.v1.*.entity")
@EnableJpaRepositories(
basePackages = "com.centrica.nebula.accounting.core.v1.*.repository",
repositoryBaseClass = SimappeRepositoryImpl.class
)
@EnableMongoRepositories("com.centrica.nebula.accounting.core.v1.kafka")
Responsabilidad: Punto de entrada de Spring Boot. Configura el contexto de la aplicacion.
Reglas identificadas:
| Anotacion | Proposito | Por que |
|---|---|---|
exclude = DataSourceAutoConfiguration |
Deshabilita auto-config de DataSource | Multi-tenancy maneja DataSource dinamicamente |
@EnableDiscoveryClient |
Registro en Eureka | Todos los servicios deben registrarse |
@EnableFeignClients |
Cliente HTTP declarativo | Comunicacion sincrona con SimappeAdmin |
@Import(MultiTenantJpaConfig) |
Multi-tenancy database-per-tenant | ADR-001 de Nebula |
@EntityScan("...v1.*.entity") |
Scan de entidades JPA | Solo escanea paquetes entity dentro de dominios |
repositoryBaseClass = SimappeRepositoryImpl |
Base de repositorios | Habilita multi-tenancy automatico en queries |
Documento normativo: Patron Arquitectura Backend
| Clase | Responsabilidad | Beans que expone |
|---|---|---|
| AppConfig | Timezone, password encoder, utilities | passwordEncoder(), propertySourcesPlaceholderConfigurer() |
| AsyncConfig | Thread pools para tareas asincronas | asyncTaskExecutor (5-15 threads), schedulerTaskExecutor (2-5 threads) |
| ClientConfig | WebClient para llamadas HTTP reactivas | webClient(), simappeAdminWebClient() + filtros de auth y logging |
| MultiTenantJpaConfig | Multi-tenancy con HikariCP por tenant | entityManagerFactory(), TenantAwareDataSource, TenantDatabaseManager |
| MongoConfiguration | Conexion a MongoDB para auditoria | mongoTemplate(), mongoDatabaseFactory() |
| KafkaAdapterConfig | Template tipado para Kafka | serializableKafkaTemplate() |
| ResourceLockConfiguration | Validacion de Redis locks al arranque | Test de conectividad y lock acquisition |
| DockerMongoHealthConfig | Override health check Mongo en dev | mongoHealthIndicator() (solo perfil dev) |
Esta es la configuracion mas critica del servicio:
JWT Token (tenantId: "acme_prod")
|
v
TenantContext.setCurrentTenant("acme_prod")
|
v
TenantAwareDataSource.determineCurrentLookupKey()
|
v
HikariCP Pool (acme_prod) --> PostgreSQL Database (acme_prod)
Parametros HikariCP por tenant:
Documento normativo: Arquitectura Software Nebula - ADR-001 (Multi-Tenancy a Nivel de Base de Datos)
ClientConfig configura dos filtros criticos:
Documento normativo: Arquitectura Software Nebula - ADR-003 (Comunicacion Sincrona vs Asincrona)
Clase final con constantes estaticas del dominio contable:
// Longitudes de campo (validacion)
public static final int TERCERO_TIPO_DOCUMENTO_MAX = 50;
public static final int RAZON_SOCIAL_MAX = 500;
public static final int CENTRO_COSTOS_CODIGO_MAX_LENGTH = 10;
// Estados de negocio
public static final String ESTADO_ACTIVO = "ACTIVO";
public static final String ESTADO_INACTIVO = "INACTIVO";
// Tipos de vinculo
public static final String VINCULO_CLIENTE = "CLIENTE";
public static final String VINCULO_PROVEEDOR = "PROVEEDOR";
// Claves de mensaje i18n
public static final String MSG_ERROR_CODIGO_INMUTABLE = "error.codigo.inmutable";
public static final String MSG_ERROR_ELIMINACION_REFERENCIAS = "error.eliminacion.referencias";
Regla: Todas las constantes de dominio van en esta clase. No se usan strings literales en la logica de negocio.
Componente Spring para resolucion de mensajes internacionalizados:
@Component
public class AccountingMessages {
@Autowired private MessageSource messageSource;
public String get(String messageKey) { ... }
public String get(String messageKey, Object... args) { ... }
// Atajos para mensajes frecuentes
public String errorCodigoInmutable() { return get(MSG_ERROR_CODIGO_INMUTABLE); }
public String errorEliminacionReferencias() { return get(MSG_ERROR_ELIMINACION_REFERENCIAS); }
}
Archivo de mensajes: src/main/resources/messages_es.properties
Documento normativo: Estandares de Desarrollo
Implementa HealthIndicator y registra metricas Micrometer para Prometheus:
| Metrica | Tipo | Proposito |
|---|---|---|
nebula.accounting.requests.total |
Counter | Total de requests recibidos |
nebula.accounting.errors.total |
Counter | Total de errores |
nebula.accounting.success.total |
Counter | Total exitosos |
nebula.accounting.operations.active |
Gauge | Operaciones en progreso |
nebula.accounting.operations.processed |
Gauge | Total procesadas |
nebula.accounting.operation.duration |
Timer | Duracion de operaciones |
Uso en componentes:
applicationMetrics.incrementRequests();
try {
// logica de negocio
applicationMetrics.incrementSuccess();
} catch (Exception e) {
applicationMetrics.incrementErrors();
}
@RestController
@RequestMapping("/api/v1/centro-costos")
@Produces(MediaType.APPLICATION_JSON_VALUE)
public class CentroCostosController extends SimappeController {
private final CentroCostosService centroCostosService;
@GetMapping("/read")
@Operation(summary = "Listar centros de costos")
public ResponseEntity<List<CentroCostosDto>> read(HttpServletRequest request) {
return new ResponseEntity<>(centroCostosService.read(request), HttpStatus.OK);
}
@PostMapping("/create")
@Operation(summary = "Crear centro de costos")
public ResponseEntity<CentroCostosDto> create(
@Valid @RequestBody CentroCostosDto dto,
HttpServletRequest request) {
return new ResponseEntity<>(centroCostosService.create(dto, request), HttpStatus.CREATED);
}
}
| Regla | Descripcion | Referencia |
|---|---|---|
Extiende SimappeController |
Hereda utilidades HTTP y manejo de errores | Patron Backend |
| NO contiene logica de negocio | Solo recibe HTTP y delega al Service | Patron Backend |
| Versionado en URL | /api/v1/ |
Estandares de Desarrollo |
| Anotaciones OpenAPI en cada endpoint | @Operation, @ApiResponse |
Reglas Desarrollo Servicios |
@Valid en DTOs de entrada |
Validacion Jakarta Bean Validation | Estandares Mapeo DTO-Entity |
HttpServletRequest como parametro |
Necesario para extraer tenant del JWT | Patron Backend |
Retorna ResponseEntity<Dto> |
Nunca retorna Entity directamente | Estandares Mapeo DTO-Entity |
Cada controller de dominio expone los siguientes endpoints:
| Metodo | Ruta | Proposito |
|---|---|---|
GET / |
index() |
Health check del servicio |
GET /read |
read() |
Listar todos los registros |
GET /get?id= |
get() |
Obtener por ID |
POST /create |
create() |
Crear nuevo registro |
PUT /update?id= |
update() |
Actualizar existente |
DELETE /delete?id= |
delete() |
Eliminar registro |
POST /page |
getPageDtoQuery() |
Consulta paginada con filtros |
@Service
public class CentroCostosService {
private final CentroCostosComponent centroCostosComponent;
public List<CentroCostosDto> read(HttpServletRequest request) {
return centroCostosComponent.read(request);
}
public CentroCostosDto create(CentroCostosDto dto, HttpServletRequest request) {
return centroCostosComponent.create(dto, request);
}
// ... delegacion directa para cada operacion
}
| Regla | Descripcion |
|---|---|
| Solo delega al Component | NO contiene logica de negocio |
Anotado con @Service |
Bean de Spring |
| Puede orquestar multiples Components | Si la operacion involucra varios dominios |
| NO accede a repositorios directamente | Siempre via Component |
Por que existe si solo delega? El Service es el punto de orquestacion para operaciones que involucran multiples Components. Ejemplo futuro: un
AsientoContableServicepodria orquestarAsientoComponent+CentroCostosComponent+TerceroComponenten una sola transaccion.
Documento normativo: Patron Arquitectura Backend — ADR-002
@ConnectionContext // Multi-tenancy: resuelve tenant del JWT automaticamente
@EntityIdSupport // Conversion automatica de IDs (LongId)
@Component
@Slf4j
public class CentroCostosComponent extends SimappeService
implements CrudHtppServletRequestComponent<CentroCostosDto, Long> {
private final CentroCostosRepository centroCostosRepository;
private final TipoCentroCostosRepository tipoCentroCostosRepository;
private final CentroCostosLightLoader centroCostosLightLoader;
private final AuditLogService auditLogService;
private final AccountingMessages messages;
@Transactional(readOnly = true)
@Observed @Timed("nebula.accounting.cdc.read")
public List<CentroCostosDto> read(HttpServletRequest request) {
List<CentroCostosEntity> entities = centroCostosRepository.findAll();
List<CentroCostosDto> dtos = centroCostosLightLoader.loadForListEntity(entities);
auditLogService.sendMessage(/* audit event READ */);
return dtos;
}
}
| Anotacion | Proposito | Fuente |
|---|---|---|
@ConnectionContext |
Activa resolucion automatica de tenant desde JWT | SimappeCommons |
@EntityIdSupport |
Habilita conversion de Long a LongId y viceversa |
SimappeCommons |
@Component |
Registro como bean Spring | Spring Framework |
@Slf4j |
Logger via Lombok | Lombok |
SimappeService (SimappeCommons)
^
|-- extends
|
CentroCostosComponent
|-- implements CrudHtppServletRequestComponent<CentroCostosDto, Long>
SimappeService: Provee utilidades de mapeo (loadForListEntity, loadForEntity) y acceso al contexto HTTP.CrudHtppServletRequestComponent<D, ID>: Contrato que obliga a implementar read(), get(), create(), update(), delete(), getPageDtoQuery().// CentroCostosComponent.update()
if (cambiaEstado && nuevoEstado == INACTIVO) {
checkMovimientosAsociados(id);
// Si tiene movimientos -> SimappeException
// Mensaje: "No se puede inactivar un centro con movimientos asociados"
}
No se puede inactivar un Centro de Costos si tiene movimientos contables asociados.
// CentroCostosComponent.update()
if (!entityActual.getCodigo().equals(dto.getCodigo())) {
throw new SimappeException(messages.errorCodigoInmutable());
}
El campo
codigoes inmutable despues de la creacion. No se permite modificar.
// TipoCentroCostosComponent.delete()
long count = centroCostosRepository.countByIdTipoCentroCostos(id);
if (count > 0) {
throw new SimappeException(messages.errorEliminacionReferencias());
}
No se puede eliminar un Tipo de Centro de Costos si existen Centros de Costos que lo referencian.
| Operacion | Anotacion | Justificacion |
|---|---|---|
read(), get() |
@Transactional(readOnly = true) |
Solo lectura, optimiza conexion |
create(), update(), delete() |
@Transactional |
Escritura con rollback automatico en exception |
getPageDtoQuery() |
@Transactional(readOnly = true) |
Paginacion es lectura |
Toda operacion CRUD registra un evento de auditoria via Kafka:
auditLogService.sendMessage(SimappeUtilities.createAuditLogMessage(
"create", // accion
"Centro de costos creado: " + dto.getCodigo(), // detalle
EventType.CREATE, // tipo de evento
this.getClass().getCanonicalName(), // recurso
request.getHeader(SimappeConstant.AUTHORIZATION) // token JWT
));
Flujo: Component → AuditLogService → Kafka topic user-events → AuditLogListener → MongoDB
Documento normativo: Arquitectura Software Nebula - ADR-005 (Kafka para Event-Driven Architecture)
@Entity
@Table(name = "centros_costos", schema = "accounting")
@Data @Builder @NoArgsConstructor @AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class CentroCostosEntity extends BusinessBaseEntity<LongId> {
@Column(name = "codigo", nullable = false)
private Long codigo;
@Column(name = "nombre", nullable = false, length = 100)
private String nombre;
@Column(name = "estado_centro", nullable = false, length = 10)
@Enumerated(EnumType.STRING)
private EstadoCentroCostosEnum estadoCentro;
@Column(name = "id_tipo_centro_costos")
private Long idTipoCentroCostos;
}
| Regla | Descripcion | Referencia |
|---|---|---|
Extiende BusinessBaseEntity<LongId> |
Hereda id, createdAt, createdBy, updatedAt, updatedBy | SimappeCommons |
@Table(schema = "accounting") |
Schema fijo por dominio. Multi-tenancy opera a nivel de database, no schema | ADR-001 |
Lombok: @Data @Builder |
Genera getters/setters/builder automaticamente | Estandares Desarrollo |
Enums como @Enumerated(STRING) |
Legibilidad en BD. Nunca ORDINAL | Convencion |
FK como Long (no @ManyToOne) |
Relaciones por ID, no por navegacion JPA. El LightLoader resuelve | Estandares Mapeo DTO-Entity |
Sin @ManyToOne ni @OneToMany |
Evita fetching implicito y N+1 queries | Patron Backend |
public interface CentroCostosRepository
extends SimappeRepository<CentroCostosEntity, LongId> {
Optional<CentroCostosEntity> findByCodigo(Long codigo);
boolean existsByCodigo(Long codigo);
List<CentroCostosEntity> findByIdTipoCentroCostos(Long idTipoCentroCostos);
long countByIdTipoCentroCostos(Long idTipoCentroCostos);
@Query("SELECT c FROM CentroCostosEntity c ORDER BY c.codigo ASC")
List<CentroCostosEntity> findAllOrderByCodigo();
@Query("SELECT c FROM CentroCostosEntity c WHERE c.estadoCentro = 'ACTIVO'")
List<CentroCostosEntity> findAllActive();
}
| Regla | Descripcion |
|---|---|
Extiende SimappeRepository<Entity, LongId> |
Nunca JpaRepository directamente. SimappeRepository aplica filtro de tenant automatico |
| Metodos derivados (Spring Data) | findBy..., existsBy..., countBy... para queries simples |
@Query JPQL |
Para queries complejas. Siempre JPQL, no SQL nativo |
Sin SQL nativo (nativeQuery = true) |
Prohibido salvo aprobacion explicita del arquitecto |
| Multi-tenancy transparente | Todas las queries se ejecutan contra la BD del tenant activo |
Sin LightLoader, cargar 100 Centros de Costos con su Tipo genera:
SELECT * FROM centros_costos (100 resultados)SELECT * FROM tipos_centro_costos WHERE id = ? (una por cada centro)@ConnectionContext
@EntityIdSupport
@Component
public class CentroCostosLightLoader
extends AbstractLoadModels<Long, CentroCostosDto, CentroCostosEntity> {
public List<CentroCostosDto> enrichWithTipos(List<CentroCostosDto> dtos) {
// 1. Extraer IDs unicos de tipos
Set<Long> tipoIds = dtos.stream()
.map(CentroCostosDto::getIdTipoCentroCostos)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
// 2. UNA sola query para todos los tipos
Map<Long, TipoCentroCostosDto> tiposMap = tipoCentroCostosRepository
.findAllById(tipoIds)
.stream()
.collect(Collectors.toMap(e -> e.getId().getValue(), this::mapToDto));
// 3. Enriquecer cada DTO con lookup O(1)
dtos.forEach(dto -> {
TipoCentroCostosDto tipo = tiposMap.get(dto.getIdTipoCentroCostos());
if (tipo != null) dto.setTipoCentroCostos(tipo);
});
return dtos;
}
}
Resultado: 2 queries en lugar de 101 (1 para centros + 1 batch para tipos).
| Situacion | Crear LightLoader? |
|---|---|
| Entity tiene FK a otra tabla y se muestra en listados | SI |
| Entity es autonoma (sin FKs) | NO |
| FK se usa solo en operaciones individuales (get by id) | NO (un query extra es aceptable) |
FK aparece en paginacion (/page) |
SI |
Documento normativo: Estandares Mapeo DTO-Entity y Diseno Nebula Common/Shared
Component Kafka Listener MongoDB
| | | |
+-- sendMessage() -----> | | |
| (topic: user-events) | | |
| +-- deliver -----------> | |
| | +-- save() ---------> |
| | | (AuditLog doc) |
@Service
public class AuditLogListener {
private final AuditLogRepository auditLogRepository;
@KafkaListener(topics = "user-events", containerFactory = "kafkaListenerContainerFactory")
public void receive(AuditLogMessage message) {
AuditLog auditLog = new AuditLog();
auditLog.setAction(message.getAction());
auditLog.setDetails(message.getDetails());
auditLog.setEventType(message.getEventType());
auditLog.setTimestamp(new Date());
auditLogRepository.save(auditLog);
}
}
public interface AuditLogRepository extends MongoRepository<AuditLog, String> {
long count();
}
Documento normativo: Arquitectura Software Nebula - ADR-005 (Kafka Event-Driven)
spring:
application:
name: nebula-accounting-core
profiles:
active: common,${APP_PROFILE:localhost}
main:
allow-bean-definition-overriding: true
| Perfil | Archivo | Uso |
|---|---|---|
localhost |
application-localhost.yml |
Desarrollo local sin Docker |
dev |
application-dev.yml |
Ambiente DEV (Config Server) |
prod |
application-prod.yml |
Produccion (Config Server) |
# messages_es.properties
error.codigo.inmutable=El codigo no puede ser modificado una vez creado
error.eliminacion.referencias=No se puede eliminar: existen registros asociados
error.cambio.estado.documentos=No se puede cambiar el estado: tiene documentos asociados
error.movimiento.inactivo=No se puede registrar movimiento en un centro inactivo
error.tercero.duplicado=Ya existe un tercero con este documento
-- Schema: accounting (por tenant database)
CREATE TABLE tipos_centro_costos (
id BIGSERIAL PRIMARY KEY,
codigo VARCHAR(8) NOT NULL UNIQUE,
nombre VARCHAR(50) NOT NULL,
created_at TIMESTAMP DEFAULT NOW(),
created_by VARCHAR(100),
updated_at TIMESTAMP DEFAULT NOW(),
updated_by VARCHAR(100)
);
CREATE TABLE centros_costos (
id BIGSERIAL PRIMARY KEY,
codigo BIGINT NOT NULL UNIQUE,
nombre VARCHAR(100) NOT NULL,
estado_centro VARCHAR(10) NOT NULL DEFAULT 'ACTIVO',
id_tipo_centro_costos BIGINT REFERENCES tipos_centro_costos(id),
created_at TIMESTAMP DEFAULT NOW(),
created_by VARCHAR(100),
updated_at TIMESTAMP DEFAULT NOW(),
updated_by VARCHAR(100)
);
| Patron | Donde se Aplica | Documento Normativo |
|---|---|---|
| Controller → Service → Component → Repository | Todas las capas | Patron Backend |
| Multi-Tenancy Database-per-Tenant | MultiTenantJpaConfig + @ConnectionContext | Arq. Software Nebula - ADR-001 |
| LightLoader (N+1 Prevention) | Relaciones FK en listados | Estandares Mapeo DTO-Entity |
| Event-Driven Audit | AuditLogService → Kafka → MongoDB | Arq. Software Nebula - ADR-005 |
| DTO Enrichment | Entity → DTO via ModelMapper + LightLoader | Estandares Mapeo DTO-Entity |
| Service Facade | Service delega a Component(s) | Patron Backend - ADR-002 |
| Observability | @Observed @Timed + ApplicationMetrics | Reglas Desarrollo Servicios |
| # | Regla | Capa | Referencia |
|---|---|---|---|
| 1 | Controller NO tiene logica de negocio | Controller | Patron Backend |
| 2 | Service solo orquesta, NO tiene logica | Service | Patron Backend |
| 3 | Component tiene TODA la logica de negocio | Component | Patron Backend |
| 4 | @ConnectionContext obligatorio en Component |
Component | Reglas Desarrollo |
| 5 | Nunca retornar Entity al Controller | DTO | Estandares Mapeo |
| 6 | Sin @ManyToOne ni @OneToMany en entities |
Entity | Patron Backend |
| 7 | Repository extiende SimappeRepository (no JpaRepository) |
Repository | Reglas Desarrollo |
| 8 | Toda operacion CRUD registra evento de auditoria | Component | Arq. Software - ADR-005 |
| 9 | @Transactional(readOnly=true) para lecturas |
Component | Estandares Desarrollo |
| 10 | Constantes de dominio en clase *DomainConstants |
Constants | Estandares Desarrollo |
| 11 | Mensajes via i18n, nunca strings hardcodeados | Util | Estandares Desarrollo |
| 12 | Endpoints documentados con OpenAPI/Swagger | Controller | Reglas Desarrollo |
Al crear una nueva feature en un microservicio Nebula, verificar:
extends BusinessBaseEntity<LongId>, @Table(schema="..."), Lombokextends SimappeRepository<Entity, LongId>@ConnectionContext, @EntityIdSupport, extends SimappeServiceextends SimappeController, endpoints CRUD, @Operation en cada metodo*DomainConstants.javamessages_es.properties@Observed @Timed en operaciones del ComponentauditLogService.sendMessage() en cada operacion CRUD@Transactional / @Transactional(readOnly=true) correctamente aplicada// RN-XX: en el ComponentDocumento complementario: Guia Crear Feature Backend
| Documento | Seccion | Relevancia |
|---|---|---|
| Patron Arquitectura Backend | 01 | Patron de capas obligatorio |
| Estandares de Desarrollo | 01 | Convenciones de nombrado y estructura |
| Reglas de Desarrollo de Servicios | 01 | Reglas obligatorias de estructura |
| Estandares Mapeo DTO-Entity | 01 | Patron de mapeo y LightLoader |
| Diseno Nebula Common/Shared | 01 | Librerias compartidas |
| Arquitectura Software Nebula | 01 | ADRs de arquitectura (Multi-tenancy, Kafka, etc.) |
| Estructura de Proyecto y Paquetes | 03 | Organizacion de paquetes |
| Guia Crear Feature Backend | 05 | Paso a paso para implementar |
| Flujo de Desarrollo Nebula | 05 | Workflow diario |
| Version | Fecha | Autor | Descripcion |
|---|---|---|---|
| 1.0.0 | 2026-03-16 | Carlos Torres | Creacion del documento basado en analisis de nebula-accounting-core |