Pure Assert

July 16, 2026 Β· View on GitHub

Maven Central License Java

A lightweight, expressive Java library for pure input assertions, designed for domains that choose to be technically agnostic.

Ensure your code is Always Valid with a fluent API that prioritizes expressiveness, longevity, and semantic error richness over framework-driven validation.

✨ Features

  • 🚫 Zero Dependencies – Pure Java, no transitive pollution
  • 🎯 Typed Exceptions – Get StringTooShortException instead of IllegalArgumentException
  • πŸ“ Rich Metadata – Exceptions contain field name, invalid value, and constraints
  • πŸ”— Fluent API – Chainable, expressive, self-documenting code
  • πŸ—οΈ DDD Optimized – Perfect for critical business invariants

πŸ“¦ Installation

Maven

<dependency>
  <groupId>io.github.sympol</groupId>
  <artifactId>pure-assert</artifactId>
  <version>1.1.0</version>
</dependency>

Gradle

implementation 'io.github.sympol:pure-assert:1.1.0'

πŸš€ Quick Start

import io.github.sympol.pure.asserts.Assert;

public class User {
    private final String email;
    private final int age;

    public User(String email, int age) {
        this.email = Assert.field("email", email)
                        .notBlank()
                        .email()
                        .toField(); // Returns the validated value

        this.age = Assert.field("age", age)
                        .min(18)
                        .max(120)
                        .toField();
    }
}

πŸ“š Supported Assertions

TypeAvailable Methods
StringsnotBlank(), minLength(n), maxLength(n), matches(pattern), email(), url(), satisfies(predicate)
Numbersmin(n), max(n), isGreaterThanOrEqualTo(n), isLessThanOrEqualTo(n), positive(), strictlyPositive(), satisfies(predicate)
CollectionsnotEmpty(), minSize(n), maxSize(n), noNullElement(), contains(element), uniqueElements(), satisfies(predicate)
ArraysnotEmpty(), maxSize(n), noNullElement(), satisfies(predicate)
MapsnotEmpty(), minSize(n), maxSize(n), containsKey(key), containsValue(value), satisfies(predicate)
EnumsisIn(values...), isNotIn(values...), isAnyOf(values...), isNoneOf(values...), name(), ordinal(), satisfies(predicate)
byte[]notEmpty(), maxSize(n), satisfies(predicate)
DatesinPast(), inFuture(), after(date), before(date), afterOrAt(date), beforeOrAt(date), isBetween(start, end), satisfies(predicate)
UUIDisVersion(v), isNotNil(), satisfies(predicate)

🎯 Custom Validations

Extend the validation chain using satisfies:

Assert.field("username", username)
      .notBlank()
      .satisfies(u -> u.startsWith("user_"), "Username must start with 'user_'");

πŸ’¬ Custom Error Messages

Use withMessage(String) to override default error messages for better localization:

Assert.field("age", age)
      .withMessage("L'Γ’ge doit Γͺtre au moins 18 ans")
      .min(18);

Assert.field("email", email)
      .withMessage("L'email n'est pas valide")
      .notBlank()
      .email();

The custom message is used only when the assertion fails, then reset for subsequent assertions.

πŸ”’ Final Fields Support

The library is designed to work seamlessly with final fields. Use .toField() (or .value()) to assign the validated value directly in the constructor:

public class Order {
    private final String orderId;
    private final BigDecimal amount;
    private final LocalDateTime createdAt;

    public Order(String orderId, BigDecimal amount, LocalDateTime createdAt) {
        this.orderId = Assert.field("orderId", orderId)
                            .notBlank()
                            .toField();  // Clear intent: assigning to a field

        this.amount = Assert.field("amount", amount)
                           .notNull()
                           .isGreaterThanOrEqualTo(BigDecimal.ZERO)
                           .toField();

        this.createdAt = Assert.field("createdAt", createdAt)
                              .notNull()
                              .inPast()
                              .toField();
    }
}

This pattern ensures your domain objects are Always Valid while keeping fields immutable.

πŸ†š Comparison with Alternatives

FeaturePure AssertGuava / ApacheJakarta Validator
ExceptionsTyped & RichGenericConstraintViolation
ValidationImmediateImmediateDeferred
DependenciesZeroStable / MatureSpecification
Primary UseCore DomainUtilityInfrastructure / UX

🧠 When Should I Use Pure Assert?

βœ… Strategic Choice

  • Complex business core
  • Long-lived applications (5+ years)
  • Critical invariants (finance, legal, health)
  • DDD / Hexagonal architectures

🚫 Consider Alternatives

  • Simple CRUD / Data-centric apps
  • Strong time-to-market pressure
  • Need for error aggregation
  • Highly localized/i18n UI forms

πŸ’‘ For peripheral validation (DTOs, UI), Jakarta Validation remains the standard. For core domain invariants where "Always Valid" is key, pure-assert provides stronger guarantees.

πŸ›οΈ Maintenance & Lock-in

We understand that adding a dependency to your domain is a major architectural decision.

  • Minimalist API: The surface is intentionally small to limit breaking changes.
  • Easy Internalization: The core logic is under 2000 lines of pure Javaβ€”if the library is ever abandoned, it is trivial to copy the source into your project.
  • Semantic Versioning: We strictly follow SemVer. No breaking changes without a major version bump.

πŸ›οΈ Clean Architecture Integration

To use this library while enforcing a "Zero Dependency" rule in your domain layer:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-enforcer-plugin</artifactId>
    <version>3.0.0</version>
    <executions>
        <execution>
            <id>enforce-no-external-deps</id>
            <goals>
                <goal>enforce</goal>
            </goals>
            <configuration>
                <rules>
                    <bannedDependencies>
                        <excludes>
                            <exclude>*</exclude>
                        </excludes>
                        <includes>
                            <include>*:*:*:*:test</include>
                            <!-- Explicitly allow pure-assert (zero transitive deps) -->
                            <include>io.github.sympol:pure-assert</include>
                        </includes>
                    </bannedDependencies>
                </rules>
            </configuration>
        </execution>
    </executions>
</plugin>

πŸ“– Deep Dive

For a detailed technical explanation of the philosophy behind this library and a comparison with Jakarta Validation/Guava, read our featured article: Mastering Domain Invariants: How pure-assert enhances DDD and Clean Architecture

πŸ“‹ Changelog

1.1.0 (2026-07-16)

New types:

  • EnumAsserter β€” field(String, Enum) with isIn(), isNotIn(), isAnyOf(), isNoneOf(), name(), ordinal(), satisfies()
  • ByteArrayAsserter β€” field(String, byte[]) with notEmpty(), maxSize(), satisfies()
  • LocalDateTimeAsserter β€” field(String, LocalDateTime) with inPast(), inFuture(), after(), before(), afterOrAt(), beforeOrAt(), isBetween(), satisfies()

New methods on existing types:

  • isGreaterThanOrEqualTo(n) / isLessThanOrEqualTo(n) on all numeric asserters
  • toField() alias on all asserters for final field assignment
  • withMessage(String) for custom error messages (String, Integer, Long, Float, Double, BigDecimal)
  • minSize(), contains(), uniqueElements() on CollectionAsserter
  • minSize(), containsKey(), containsValue() on MapAsserter
  • afterOrAt(), beforeOrAt(), isBetween() on LocalDate and LocalDateTime
  • satisfies() on ArrayAsserter

New exceptions:

  • TooFewElementsException with TOO_FEW_ELEMENTS error type

Fixes:

  • Javadoc typos corrected (~126 occurrences)
  • LocalDateAsserter now uses proper time exceptions instead of deprecated RequiredValueException
  • UUID exceptions now return dedicated error types (UUID_IS_NIL, UUID_VERSION_MISMATCH)

See CHANGELOG.md for full details.

1.0.0 (2026-02-06)

Initial stable release with fluent API for Strings, Numbers, Collections, Maps, Dates, and UUIDs.


🀝 Contributing

Contributions are welcome! Please read CONTRIBUTING.md for guidelines.

πŸ“„ License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.