Skip to content

ahincho/nova-java-notifications

Repository files navigation

nova-java-notifications

Framework-agnostic Java library for unified multi-channel notifications (Email, SMS, Push, Slack) with pluggable providers.

The pure library (this repo) plus three framework starters (-spring-boot-starter, -quarkus-extension, -micronaut-module) plus three example apps (examples/demo-notifications-{spring-boot,quarkus,micronaut}) constitute the complete Nova Platform notifications module — the first concrete implementation of a Nivel 1 pure library per docs/adrs/shared/ADR-001-arquitectura-meta-framework-cinco-niveles.md and docs/adrs/java/ADR-015-librerias-puras-sin-dependencias-framework.md.

Channels

Channel Required by PDF? Providers bundled
Email Yes SendGrid, Mailgun
SMS Yes Twilio
Push Yes Firebase Cloud Messaging
Slack Optional Slack Incoming Webhooks

Cross-cutting features

  • Retry with exponential backoff — only on transient provider errors.
  • Circuit breaker — 3-state (CLOSED/OPEN/HALF_OPEN) with configurable threshold.
  • Rate limiting — token bucket per provider.
  • Template resolution{{variable}} syntax, pluggable resolver.
  • Pub/Sub of state events — observers can subscribe to sent/failed/retrying events without coupling to the library.

Architecture

Hexagonal / ports-and-adapters:

src/main/java/pe/edu/nova/java/libs/notifications/
  domain/              pure domain: Notification (sealed), Value Objects, Result, events
    channel/           NotificationChannel enum
    model/             sealed interface + records per channel
    vo/                EmailAddress, PhoneNumber, DeviceToken, Subject, MessageBody, NotificationId, SlackWebhookUrl
    result/            NotificationResult (Result type)
    event/             sealed NotificationEvent + 3 records
    error/             NotificationException hierarchy with ErrorCode
                       + DomainMessages (bilingual i18n)
  application/         use cases, ports, facade
  infrastructure/      configuration, factory, strategy, providers, resilience, templates, events
                       + i18n/Messages

What was built for this technical challenge

This repository is the core artifact evaluated by the technical challenge (Reto-Tecnico-Backend.pdf). It is the artifact that fulfils every "innegociable" constraint of §2.1 of the PDF.

Role in the Nova Platform

This is the Nivel 1 (pure library) of the Nova Platform notifications module, per the meta-architecture described in docs/adrs/shared/ADR-001-arquitectura-meta-framework-cinco-niveles.md and docs/adrs/java/ADR-015-librerias-puras-sin-dependencias-framework.md. The seven repositories that constitute the full deliverable are:

Repo Nivel Role
nova-java-notifications (this) 1 Pure library, framework-agnostic
nova-java-notifications-spring-boot-starter 2 Spring Boot 4.1 auto-configuration
nova-java-notifications-quarkus-extension 2 Quarkus 3.33 LTS CDI extension
nova-java-notifications-micronaut-module 2 Micronaut 5 module
examples/demo-notifications-spring-boot 3 Spring Boot example app
examples/demo-notifications-quarkus 3 Quarkus example app
examples/demo-notifications-micronaut 3 Micronaut example app

What this repository delivers

  • Four notification channels with a unified interface (NotificationFacade.send(...)):
    • Email — SendGrid + Mailgun providers, with subject + HTML body.
    • SMS — Twilio provider.
    • Push — Firebase Cloud Messaging provider.
    • Slack — Incoming Webhooks provider (optional per §2.2 of the challenge PDF).
  • Hexagonal / ports-and-adapters architecture: domain (sealed Notification, value objects, NotificationResult, events, error hierarchy, bilingual i18n), application (use cases, ports, facade), infrastructure (configuration, factory, strategy, providers, resilience decorators, templates, event publisher, i18n).
  • Pluggable providers behind the NotificationProvider port: adding a new provider is purely additive (no modification of the existing ones) — satisfies OCP and the "agregar un canal sin modificar lo existente" criterion of §2.3 of the challenge.
  • Cross-cutting resilience:
    • Retry with exponential backoff (only on transient provider errors).
    • 3-state circuit breaker (CLOSED / OPEN / HALF_OPEN) with configurable threshold and open-duration.
    • Per-provider token-bucket rate limiter.
  • Template resolution ({{variable}} syntax, pluggable resolver).
  • Pub/Sub of state events — observers subscribe to sent, failed, retrying without coupling to the library.
  • Selective bilingual i18n — code, identifiers, and Javadoc are English; user-facing exception messages and log entries have both MessagesBundle.properties (English, default) and MessagesBundle_es.properties (Spanish).
  • Credential masking in toString() and SLF4J logging — API keys, auth tokens, webhook URLs are redacted in every adapter. Verified by logback ListAppender tests in ProviderLoggingMaskingTest.

Quality gates verified

  • 129 unit tests, 0 failures across 28 test files (domain VOs/models/events/results/errors, application use cases + facade, infrastructure factory/strategy, every provider with logging capture, resilience decorators, template resolver, logging event publisher, ArchUnit test enforcing zero framework leakage in production code).
  • 87.29 % line coverage (769 / 881 lines), well above the 80 % JaCoCo gate enforced at the verify phase.
  • MainTest.java in the default package with a single assertFalse, per the literal requirement (R-MT) of §2.5 of the challenge.
  • CI/CD Pipeline on GitHub Actions: Maven build + SonarCloud analysis + matrix build across Java 21 and Java 25 (green at the time of submission).

How to reproduce the build

./mvnw clean verify

The verify phase runs: compile + test + JaCoCo coverage check (80 % minimum) + checkstyle + sources jar + javadoc jar + integration tests. After ./mvnw install, the artifact is available at ~/.m2/repository/pe/edu/nova/java/libs/nova-notifications/1.0.0/ and is consumed by the three framework starters (each published to GitHub Packages with pe.edu.nova.java.starters groupId).

Build

JDK 21+ (tested on 25) and Maven (T-02 of the challenge PDF) via the wrapper:

./mvnw compile   # compile production code
./mvnw test      # run unit tests + JaCoCo report (report phase)
./mvnw verify    # full check (JaCoCo 80% coverage gate + checkstyle + tests)
./mvnw package   # verify + jar + sources jar + javadoc jar
./mvnw install   # package + publish to ~/.m2/repository for the starters to consume

The three framework starters (-spring-boot-starter, -quarkus-extension, -micronaut-module) and the three demo apps remain on Gradle — see "Decision log" below for why the build tool differs between this repo and the rest of the notifications module.

Quality gates

  • 129 unit tests, 0 failures.
  • 87.29% line coverage (769 / 881 lines), well above the 80% JaCoCo gate.
  • 28 test files: domain VOs, models, events, results, error hierarchy + i18n; application use cases + facade; infrastructure factory, strategy, every provider (SendGrid, Mailgun, Twilio, Firebase, Slack) with logback-ListAppender capture to verify API keys/credentials are NEVER logged in plain text; resilience decorators (retry / circuit breaker / rate limit); template resolver; logging event publisher; architecture test (ArchUnit enforces zero framework leakage in production code).
  • MainTest.java in the default package with a single assertFalse, per the literal R-MT requirement of the challenge PDF.
  • i18n (selective bilingual): MessagesBundle.properties (English, default) and MessagesBundle_es.properties (Spanish) — code is 100% English; only user-facing messages (exceptions, log entries) are localized.

Quick start (Spring Boot consumer)

# application.yml
nova.notifications:
  enabled: true
  email:
    provider: sendgrid
    api-key: ${SENDGRID_API_KEY}
    default-sender: no-reply@example.com
  resilience:
    max-attempts: 3
@RestController
@RequiredArgsConstructor
public class NotificationsController {
    private final NotificationFacade facade; // auto-wired by the starter

    @GetMapping("/welcome")
    public NotificationResult welcome() {
        return facade.send(EmailNotification.builder()
                .from(new EmailAddress("no-reply@example.com"))
                .to(new EmailAddress("customer@example.com"))
                .subject(new Subject("Welcome"))
                .body(new MessageBody("Thanks for signing up."))
                .build());
    }
}

Decision log (NOVA-SEMVER)

  • Build tool: Maven (T-02 of the challenge PDF explicitly mandates it). This repo is the artifact actually evaluated by the challenge, so it complies with Maven literally, even though every other Nivel 1 library in Nova (and this module's own starters/demos) is Gradle. Scoping the deviation to this single repo — instead of migrating the whole notifications module, or running two build files in parallel here — keeps exactly one module on Maven (a finite, one-time migration) while the starters/demos keep Nova's Gradle consistency for what is Nova's own extension beyond the challenge's scope. A prior revision of this document justified staying on Gradle with a factually wrong claim ("the challenge mandated Gradle 7.x") — the PDF never mentioned Gradle at all, only Maven; that justification has been corrected here.
  • Spring Boot / Quarkus / Micronaut prefix: nova.notifications.*, following the same convention as the newer Nova starters (e.g. nova-observability-spring-boot-starter uses nova.observability.*). The older legacy starters (nova-mask-starter, nova-api-standard-starter) still use galaxy-training.*; a global migration is tracked in the meta-framework backlog.
  • Translation: English identifiers + comments + Javadoc, with Spanish translations available at runtime for error messages and log entries. The rest of Nova's Nivel 1 libraries have mostly Spanish Javadoc; this one breaks the convention deliberately because the challenge reviewer (external) is more likely to read English, and the meta-framework is moving toward English docs over time. Any future PRs that re-translate to Spanish will be welcomed.

Related modules

  • nova-java-notifications-spring-boot-starter — Spring Boot auto-configuration.
  • nova-java-notifications-quarkus-extension — Quarkus "colloquial" extension (no @BuildStep, plain @Singleton beans).
  • nova-java-notifications-micronaut-module — Micronaut module (no deployment/runtime split, plain @Factory / @Bean).
  • examples/demo-notifications-{spring-boot,quarkus,micronaut} — one example app per framework consuming its respective starter.

AI Assistance Attribution

This repository was produced through human-AI collaboration. The human author (Angel Eduardo Hincho Jove, ahincho@unsa.edu.pe, Universidad Nacional de San Agustín de Arequipa — UNSA) retains full responsibility for the final artifact and for every commit accepted into the repository.

Challenge context

This work was produced in response to the technical challenge described in Reto-Tecnico-Backend.pdf. Section 2.5 of the challenge mandates an explicit AI disclosure in the README when AI is used. This section fulfils that requirement (R-AI / Responsible AI disclosure) and is also aligned with:

  • Regulation (EU) 2024/1689 ("EU AI Act"), Article 3(1) (definition of "AI system") and Article 50 (transparency obligations for deployers of certain AI systems).
  • UNESCO Recommendation on the Ethics of Artificial Intelligence (2021), adopted by 193 Member States, Principle 6: Transparency and explainability.

AI tools used in this repository

Tool Provider Model / Role Access tier
GitHub Copilot GitHub / Anthropic Claude Opus 4.8, Claude Sonnet 5 (in-editor suggestions) Licensed
MiniMax Token Plan MiniMax MiniMax-M3 (the model used for long-form generation and refactoring in the OpenCode session) Paid (personal)
OpenCode anomalyco (opencode.ai) Interactive CLI harness — not a model, only the session/UI Free (CLI)
OpenSpec Fission AI Spec-driven development framework (used for the meta-framework backlog) Licensed
NotebookLM Google Gemini (cross-document synthesis of the challenge PDF and ADRs) Free
Perplexity Perplexity AI Sonar / Pro Search (lookup of latest framework versions and release dates) Free

Important distinction: OpenCode is the interactive CLI harness in which the AI-assisted development session took place (with MiniMax-M3 as the underlying model). OpenCode is not a model and not a license/subscription manager — the subscription providing access to the model is the MiniMax Token Plan listed above. The two rows are kept deliberately separate so that anyone reading the disclosure can identify exactly which entity provides the model and which entity provides the session/UI.

Scope of AI assistance in this repository

  • Drafting the initial code skeletons (sealed interfaces, value objects, port interfaces, provider stubs).
  • Drafting unit tests for value objects, the error hierarchy, the template resolver, the rate-limiter, the circuit-breaker state machine, and the i18n message bundle (Spanish / English).
  • Documentation drafts of this README and the inline Javadoc.
  • Build infrastructure snippets for the reusable CI/CD workflows in ahincho/nova-devops.
  • Cross-checking the published provider documentation (SendGrid, Mailgun, Twilio, Firebase) for the authentication-header / payload shape used in the per-provider adapters (no live API calls are made).

Human contributions (author: Angel Eduardo Hincho Jove)

The following decisions and artifacts are authored and approved by the human, not delegated to AI:

  • Architecture: hexagonal / ports-and-adapters layout, framework isolation in the core library, the five-level meta-framework (Nivel 1 = pure library, Nivel 2 = starter/extension, Nivel 3 = application) per ADR-001 and ADR-015.
  • Scope: which channels and providers are in scope for the challenge (Email / SMS / Push mandatory, Slack optional) and which features are deferred.
  • Version pinning: Java 25, Spring Boot 4.1.0, Quarkus 3.33.2.1 LTS, Micronaut 5.0.4, Gradle 9.5.1, Maven 3.9.x. Each pin was cross-checked against the latest stable release and the framework vendor's LTS roadmap.
  • Quality gates: 80 % JaCoCo coverage, Checkstyle Nova style, ArchUnit test enforcing zero framework leakage in the core library.
  • Build infrastructure: Maven for the core (T-02 of the challenge mandates Maven), Gradle 9.5.1 for the framework starters and demos (consistency with the rest of the Nova Platform).
  • Final review and approval of every commit, including a final end-to-end run of ./mvnw verify and ./gradlew build against JDK 25 before tagging the release.
  • Legal classification: the determination that the artifacts shipped here (a deterministic Java library + framework adapters + example apps) are not "AI systems" under EU AI Act Article 3(1) and therefore do not directly attract Article 50 obligations (see the legal clarification below).

Methodology

The work followed a Spec-Driven Development approach using OpenSpec:

  1. Requirements were captured as structured specifications before any code was written (CHALLENGE.md, REQUIREMENTS.md, the ADRs).
  2. AI assistance operated against those specifications, not in the abstract.
  3. The human author reviewed and approved each artifact (build, test, commit) before it was accepted into the repository.

Legal clarification (EU AI Act)

A deterministic Java notifications library does not "infer" outputs, does not generate predictions / recommendations / decisions, and does not exhibit autonomy or adaptiveness after deployment. Therefore the artifacts shipped in this repository are not "AI systems" within the meaning of Article 3(1) of Regulation (EU) 2024/1689 (the EU AI Act), and Article 50 does not directly impose obligations on them.

This disclosure is nevertheless made:

  • By contractual / academic requirement: per the R-AI requirement of the originating technical challenge (challenge PDF §2.5).
  • Voluntarily, in alignment with the spirit of the EU AI Act transparency principles and UNESCO Principle 6.
  • In the interest of authorship transparency for the open-source community.

Canonical disclosure

The full Nova Platform AI attribution (covering every repository in the workspace — pure libraries, framework adapters, demos, tooling and documentation) lives in a single canonical file at the workspace root:

../../AI-ATTRIBUTION.md

This per-repository section is a compact summary that points back to that canonical file as the source of truth for the full disclosure; the legal analysis and the human-contributions audit are not duplicated in every repository on purpose.

Change log

  • 2026-07-15 — Initial disclosure created.

About

Nova Notifications core library: pure-Java (no framework), framework-agnostic facade for Email/SMS/Push/Slack with Resilience4j-style retry + circuit breaker + rate limit. Published to GitHub Packages.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages