Pure Assert
July 16, 2026 Β· View on GitHub
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
StringTooShortExceptioninstead ofIllegalArgumentException - π 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
| Type | Available Methods |
|---|---|
| Strings | notBlank(), minLength(n), maxLength(n), matches(pattern), email(), url(), satisfies(predicate) |
| Numbers | min(n), max(n), isGreaterThanOrEqualTo(n), isLessThanOrEqualTo(n), positive(), strictlyPositive(), satisfies(predicate) |
| Collections | notEmpty(), minSize(n), maxSize(n), noNullElement(), contains(element), uniqueElements(), satisfies(predicate) |
| Arrays | notEmpty(), maxSize(n), noNullElement(), satisfies(predicate) |
| Maps | notEmpty(), minSize(n), maxSize(n), containsKey(key), containsValue(value), satisfies(predicate) |
| Enums | isIn(values...), isNotIn(values...), isAnyOf(values...), isNoneOf(values...), name(), ordinal(), satisfies(predicate) |
| byte[] | notEmpty(), maxSize(n), satisfies(predicate) |
| Dates | inPast(), inFuture(), after(date), before(date), afterOrAt(date), beforeOrAt(date), isBetween(start, end), satisfies(predicate) |
| UUID | isVersion(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
| Feature | Pure Assert | Guava / Apache | Jakarta Validator |
|---|---|---|---|
| Exceptions | Typed & Rich | Generic | ConstraintViolation |
| Validation | Immediate | Immediate | Deferred |
| Dependencies | Zero | Stable / Mature | Specification |
| Primary Use | Core Domain | Utility | Infrastructure / UX |
π§ When Should I Use Pure Assert?
β Strategic Choice
|
π« Consider Alternatives
|
π‘ For peripheral validation (DTOs, UI), Jakarta Validation remains the standard. For core domain invariants where "Always Valid" is key,
pure-assertprovides 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)withisIn(),isNotIn(),isAnyOf(),isNoneOf(),name(),ordinal(),satisfies()ByteArrayAsserterβfield(String, byte[])withnotEmpty(),maxSize(),satisfies()LocalDateTimeAsserterβfield(String, LocalDateTime)withinPast(),inFuture(),after(),before(),afterOrAt(),beforeOrAt(),isBetween(),satisfies()
New methods on existing types:
isGreaterThanOrEqualTo(n)/isLessThanOrEqualTo(n)on all numeric asserterstoField()alias on all asserters for final field assignmentwithMessage(String)for custom error messages (String, Integer, Long, Float, Double, BigDecimal)minSize(),contains(),uniqueElements()on CollectionAsserterminSize(),containsKey(),containsValue()on MapAsserterafterOrAt(),beforeOrAt(),isBetween()on LocalDate and LocalDateTimesatisfies()on ArrayAsserter
New exceptions:
TooFewElementsExceptionwithTOO_FEW_ELEMENTSerror type
Fixes:
- Javadoc typos corrected (~126 occurrences)
LocalDateAsserternow uses proper time exceptions instead of deprecatedRequiredValueException- 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.