{"id":13458754,"url":"https://github.com/making/yavi","last_synced_at":"2025-05-14T19:05:03.051Z","repository":{"id":32925533,"uuid":"145549617","full_name":"making/yavi","owner":"making","description":"Yet Another Validation for Java (A lambda based type safe validation framework)","archived":false,"fork":false,"pushed_at":"2025-04-12T00:02:01.000Z","size":2630,"stargazers_count":795,"open_issues_count":21,"forks_count":62,"subscribers_count":23,"default_branch":"develop","last_synced_at":"2025-04-13T13:19:08.909Z","etag":null,"topics":["java","kotlin","validation","validation-library","validator"],"latest_commit_sha":null,"homepage":"https://yavi.ik.am","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/making.png","metadata":{"files":{"readme":"README.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}},"created_at":"2018-08-21T10:39:38.000Z","updated_at":"2025-04-13T10:16:03.000Z","dependencies_parsed_at":"2024-01-31T01:56:45.204Z","dependency_job_id":"abc0c224-104d-41d0-9ffa-32a7432565af","html_url":"https://github.com/making/yavi","commit_stats":null,"previous_names":[],"tags_count":63,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/making%2Fyavi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/making%2Fyavi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/making%2Fyavi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/making%2Fyavi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/making","download_url":"https://codeload.github.com/making/yavi/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248717309,"owners_count":21150402,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["java","kotlin","validation","validation-library","validator"],"created_at":"2024-07-31T09:00:56.788Z","updated_at":"2025-05-14T19:05:02.930Z","avatar_url":"https://github.com/making.png","language":"Java","readme":"## YAVI (*Y*et *A*nother *V*al*I*dation)\n\n[![Apache 2.0](https://img.shields.io/github/license/making/yavi.svg)](https://www.apache.org/licenses/LICENSE-2.0) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/am.ik.yavi/yavi/badge.svg)](https://maven-badges.herokuapp.com/maven-central/am.ik.yavi/yavi) [![Javadocs](https://www.javadoc.io/badge/am.ik.yavi/yavi.svg)](https://www.javadoc.io/doc/am.ik.yavi/yavi) [![Actions Status](https://github.com/making/yavi/workflows/CI/badge.svg)](https://github.com/making/yavi/actions)\n\n\n![YAVI Logo](https://user-images.githubusercontent.com/106908/120071055-66770380-c0c8-11eb-83f1-d7eff04bad54.png)\n\nYAVI (pronounced jɑ-vάɪ) \nis a lambda based type safe validation for Java. \n\n### Why YAVI?\n\nYAVI sounds as same as a Japanese slang \"YABAI (ヤバイ)\" that means awesome or awful depending on the context (like \"Crazy\").\nIf you use YAVI, you will surely understand that it means the former.\n\nThe concepts are\n\n* No reflection!\n* No (runtime) annotation!\n* Not only Java Beans!\n* Zero dependency!\n\nIf you are not a fan of [Bean Validation](https://beanvalidation.org/), YAVI will be an awesome alternative.\n\nYAVI has the following features:\n\n* Type-safe constraints, unsupported constraints cannot be applied to the wrong type\n* Fluent and intuitive API\n* Constraints on any object. Java Beans, [Records](https://openjdk.java.net/jeps/395), [Protocol Buffers](https://developers.google.com/protocol-buffers), [Immutables](https://immutables.github.io/) and anything else.\n* Lots of powerful built-in constraints\n* Easy custom constraints\n* Validation for groups, conditional validation\n* Validation for arguments before creating an object\n* Support for API and combination of validation results and validators that incorporate the concept of functional programming\n\nSee [the reference documentation](https://yavi.ik.am) for details.\n\n### Presentations\n\n* 2021-07-01 YAVIの紹介 (Japanese) [[Deck](https://docs.google.com/presentation/d/1ZcGN7qZpZ92XD6FwdHxpxJ1duF1kqilI97zq-0JpzsA/edit?usp=sharing)] [[Recording](https://www.youtube.com/watch?v=o0-u6QSBlv8)]\n\n### Getting Started\n\n\u003e This content is derived from https://hibernate.org/validator/documentation/getting-started/\n\nWelcome to YAVI.\n\nThe following paragraphs will guide you through the initial steps required to integrate\nYAVI into your application.\n\n#### Prerequisites\n\n* [Java Runtime](http://www.oracle.com/technetwork/java/index.html) \u003e= 8\n* [Apache Maven](http://maven.apache.org/)\n\n#### Project set up\n\nIn order to use YAVI within a Maven project, simply add the following dependency to\nyour `pom.xml`:\n\n```xml\n\n\u003cdependency\u003e\n    \u003cgroupId\u003eam.ik.yavi\u003c/groupId\u003e\n    \u003cartifactId\u003eyavi\u003c/artifactId\u003e\n    \u003cversion\u003e0.16.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nThis tutorial uses JUnit 5 and AssertJ. Add the following dependencies as needed:\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eorg.junit.jupiter\u003c/groupId\u003e\n    \u003cartifactId\u003ejunit-jupiter-api\u003c/artifactId\u003e\n    \u003cversion\u003e5.11.4\u003c/version\u003e\n    \u003cscope\u003etest\u003c/scope\u003e\n\u003c/dependency\u003e\n\u003cdependency\u003e\n    \u003cgroupId\u003eorg.assertj\u003c/groupId\u003e\n    \u003cartifactId\u003eassertj-core\u003c/artifactId\u003e\n    \u003cversion\u003e3.26.3\u003c/version\u003e\n    \u003cscope\u003etest\u003c/scope\u003e\n\u003c/dependency\u003e\n```\n\n#### Applying constraints\n\nLet’s dive into an example to see how to apply constraints:\n\nCreate `src/main/java/com/example/Car.java` and write the following code.\n\n```java\npackage com.example;\n\nimport am.ik.yavi.builder.ValidatorBuilder;\nimport am.ik.yavi.core.Validator;\n\npublic record Car(String manufacturer, String licensePlate, Integer seatCount) {\n    public static final Validator\u003cCar\u003e validator = ValidatorBuilder.\u003cCar\u003eof()\n            .constraint(Car::manufacturer, \"manufacturer\", c -\u003e c.notNull())\n            .constraint(Car::licensePlate, \"licensePlate\", c -\u003e c.notNull().greaterThanOrEqual(2).lessThanOrEqual(14))\n            .constraint(Car::seatCount, \"seatCount\", c -\u003e c.greaterThanOrEqual(2))\n            .build();\n}\n```\n\nThe `ValidatorBuilder.constraint` is used to declare the constraints which should be\napplied to the return values of getter for the `Car` instance:\n\n* `manufacturer` must never be null\n* `licensePlate` must never be null and must be between 2 and 14 characters long\n* `seatCount` must be at least 2\n\n\u003e You can find [the complete source code](https://github.com/making/gs-yavi) on GitHub.\n\n#### Validating constraints\n\nTo perform a validation of these constraints, you use a `Validator` instance. To\ndemonstrate this, let’s have a look at a simple unit test:\n\nCreate `src/test/java/com/example/CarTest.java` and write the following code.\n\n```java\npackage com.example;\n\nimport am.ik.yavi.core.ConstraintViolations;\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nclass CarTest {\n\n    @Test\n    void manufacturerIsNull() {\n        final Car car = new Car(null, \"DD-AB-123\", 4);\n        final ConstraintViolations violations = Car.validator.validate(car);\n\n        assertThat(violations.isValid()).isFalse();\n        assertThat(violations).hasSize(1);\n        assertThat(violations.get(0).message()).isEqualTo(\"\\\"manufacturer\\\" must not be null\");\n    }\n\n    @Test\n    void licensePlateTooShort() {\n        final Car car = new Car(\"Morris\", \"D\", 4);\n        final ConstraintViolations violations = Car.validator.validate(car);\n\n        assertThat(violations.isValid()).isFalse();\n        assertThat(violations).hasSize(1);\n        assertThat(violations.get(0).message()).isEqualTo(\"The size of \\\"licensePlate\\\" must be greater than or equal to 2. The given size is 1\");\n    }\n\n    @Test\n    void seatCountTooLow() {\n        final Car car = new Car(\"Morris\", \"DD-AB-123\", 1);\n        final ConstraintViolations violations = Car.validator.validate(car);\n\n        assertThat(violations.isValid()).isFalse();\n        assertThat(violations).hasSize(1);\n        assertThat(violations.get(0).message()).isEqualTo(\"\\\"seatCount\\\" must be greater than or equal to 2\");\n    }\n\n    @Test\n    void carIsValid() {\n        final Car car = new Car(\"Morris\", \"DD-AB-123\", 2);\n        final ConstraintViolations violations = Car.validator.validate(car);\n\n        assertThat(violations.isValid()).isTrue();\n        assertThat(violations).hasSize(0);\n    }\n}\n```\n\n`Validator` instances are thread-safe and may be reused multiple times.\n\nThe `validate()` method returns a `ConstraintViolations` instance, which you can iterate\nin order to see which validation errors occurred. The first three test methods show some\nexpected constraint violations:\n\n* The `notNull()` constraint on `manufacturer` is violated in `manufacturerIsNull()`\n* The `greaterThanOrEqual(int)` constraint on `licensePlate` is violated\n  in `licensePlateTooShort()`\n* The `greaterThanOrEqual(int)` constraint on `seatCount` is violated\n  in `seatCountTooLow()`\n\nIf the object validates successfully, `validate()` returns an empty `ConstraintViolations`\nas you can see in\n`carIsValid()`. You can also check if the validation was successful with\nthe `ConstraintViolations.isValid` method.\n\n### Always Valid model?\n\nAre you dissatisfied with the classic validation approach of creating an object (that might be invalid) first and then validating it? If you want to create a model that can only be generated and exist in a valid state, YAVI can help you achieve this as well.\n\nIn this case, create your `Car` class as follows:\n\n```java\npackage com.example;\n\nimport am.ik.yavi.arguments.Arguments3Validator;\nimport am.ik.yavi.core.Validated;\nimport am.ik.yavi.validator.Yavi;\n\npublic final class Car {\n\n\tprivate static Arguments3Validator\u003cString, String, Integer, Car\u003e validator = Yavi.arguments()\n\t\t._string(\"manufacturer\", c -\u003e c.notNull())\n\t\t._string(\"licensePlate\", c -\u003e c.notNull().greaterThanOrEqual(2).lessThanOrEqual(14))\n\t\t._integer(\"seatCount\", c -\u003e c.greaterThanOrEqual(2))\n\t\t.apply(Car::new);\n\n\tprivate final String manufacturer;\n\n\tprivate final String licensePlate;\n\n\tprivate final Integer seatCount;\n\n\tpublic static Validated\u003cCar\u003e of(String manufacturer, String licensePlate, Integer seatCount) {\n\t\treturn validator.validate(manufacturer, licensePlate, seatCount);\n\t}\n\n\tprivate Car(String manufacturer, String licensePlate, Integer seatCount) {\n\t\tthis.manufacturer = manufacturer;\n\t\tthis.licensePlate = licensePlate;\n\t\tthis.seatCount = seatCount;\n\t}\n\n\tpublic String manufacturer() {\n\t\treturn manufacturer;\n\t}\n\n\tpublic String licensePlate() {\n\t\treturn licensePlate;\n\t}\n\n\tpublic Integer seatCount() {\n\t\treturn seatCount;\n\t}\n\n}\n```\n\n`Arguments3Validator\u003cString, String, Integer, Car\u003e` is a validator that validates three arguments (`String`, `String`, `Integer`) and creates a `Car` instance only when validation succeeds.\n\nWith this approach, since the `Car` class's constructor is private, you need to create a `Car` instance through the `of` factory method. The `of` method validates the arguments before creating a `Car` instance. It returns the validation result wrapped in a `Validated` type. If validation fails, you cannot obtain a `Car` instance.\n\nThe test code changes to:\n\n```java\npackage com.example;\n\nimport am.ik.yavi.core.ConstraintViolations;\nimport am.ik.yavi.core.Validated;\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nclass CarTest {\n\n\t@Test\n\tvoid manufacturerIsNull() {\n\t\tValidated\u003cCar\u003e validated = Car.of(null, \"DD-AB-123\", 4);\n\t\tassertThat(validated.isValid()).isFalse();\n\t\tConstraintViolations violations = validated.errors();\n\t\tassertThat(violations.isValid()).isFalse();\n\t\tassertThat(violations).hasSize(1);\n\t\tassertThat(violations.get(0).message()).isEqualTo(\"\\\"manufacturer\\\" must not be null\");\n\t}\n\n\t@Test\n\tvoid licensePlateTooShort() {\n\t\tValidated\u003cCar\u003e validated = Car.of(\"Morris\", \"D\", 4);\n\t\tassertThat(validated.isValid()).isFalse();\n\t\tConstraintViolations violations = validated.errors();\n\t\tassertThat(violations).hasSize(1);\n\t\tassertThat(violations.get(0).message())\n\t\t\t.isEqualTo(\"The size of \\\"licensePlate\\\" must be greater than or equal to 2. The given size is 1\");\n\t}\n\n\t@Test\n\tvoid seatCountTooLow() {\n\t\tValidated\u003cCar\u003e validated = Car.of(\"Morris\", \"DD-AB-123\", 1);\n\t\tassertThat(validated.isValid()).isFalse();\n\t\tConstraintViolations violations = validated.errors();\n\t\tassertThat(violations).hasSize(1);\n\t\tassertThat(violations.get(0).message()).isEqualTo(\"\\\"seatCount\\\" must be greater than or equal to 2\");\n\t}\n\n\t@Test\n\tvoid carIsValid() {\n\t\tValidated\u003cCar\u003e validated = Car.of(\"Morris\", \"DD-AB-123\", 2);\n\t\tassertThat(validated.isValid()).isTrue();\n\t\tCar car = validated.value();\n\t\tassertThat(car.manufacturer()).isEqualTo(\"Morris\");\n\t\tassertThat(car.licensePlate()).isEqualTo(\"DD-AB-123\");\n\t\tassertThat(car.seatCount()).isEqualTo(2);\n\t}\n\n}\n```\n\nWould you prefer to perform validation within the constructor rather than through a factory method? (And would you like to keep using records?) This can also be achieved with YAVI.\nThis time, let's modify the `Car` class as follows:\n\n```java\npackage com.example;\n\nimport am.ik.yavi.arguments.Arguments3Validator;\nimport am.ik.yavi.validator.Yavi;\n\npublic record Car(String manufacturer, String licensePlate, Integer seatCount) {\n\n\tprivate static Arguments3Validator\u003cString, String, Integer, Car\u003e validator = Yavi.arguments()\n\t\t._string(\"manufacturer\", c -\u003e c.notNull())\n\t\t._string(\"licensePlate\", c -\u003e c.notNull().greaterThanOrEqual(2).lessThanOrEqual(14))\n\t\t._integer(\"seatCount\", c -\u003e c.greaterThanOrEqual(2))\n\t\t.apply(Car::new);\n\n\tpublic Car {\n\t\tvalidator.lazy().validated(manufacturer, licensePlate, seatCount);\n\t}\n}\n```\n\nWith this approach, validation is performed at the constructor stage, and invalid objects will not be created - instead, a `ConstraintViolationsException` will be thrown.\n\n(Using the `lazy` method prevents recursive creation of `Car` instances in the constructor even when validation succeeds. This is necessary to avoid a StackOverflow error.)\n\nThe test code changes to:\n\n```java\npackage com.example;\n\nimport am.ik.yavi.core.ConstraintViolations;\nimport am.ik.yavi.core.ConstraintViolationsException;\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatCode;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\n\nclass CarTest {\n\n\t@Test\n\tvoid manufacturerIsNull() {\n\t\tassertThatThrownBy(() -\u003e {\n\t\t\tnew Car(null, \"DD-AB-123\", 4);\n\t\t}).isInstanceOf(ConstraintViolationsException.class).satisfies(e -\u003e {\n\t\t\tConstraintViolations violations = ((ConstraintViolationsException) e).violations();\n\t\t\tassertThat(violations.isValid()).isFalse();\n\t\t\tassertThat(violations).hasSize(1);\n\t\t\tassertThat(violations.get(0).message()).isEqualTo(\"\\\"manufacturer\\\" must not be null\");\n\t\t});\n\t}\n\n\t@Test\n\tvoid licensePlateTooShort() {\n\t\tassertThatThrownBy(() -\u003e {\n\t\t\tnew Car(\"Morris\", \"D\", 4);\n\t\t}).isInstanceOf(ConstraintViolationsException.class).satisfies(e -\u003e {\n\t\t\tConstraintViolations violations = ((ConstraintViolationsException) e).violations();\n\t\t\tassertThat(violations.isValid()).isFalse();\n\t\t\tassertThat(violations).hasSize(1);\n\t\t\tassertThat(violations.get(0).message())\n\t\t\t\t.isEqualTo(\"The size of \\\"licensePlate\\\" must be greater than or equal to 2. The given size is 1\");\n\t\t});\n\t}\n\n\t@Test\n\tvoid seatCountTooLow() {\n\t\tassertThatThrownBy(() -\u003e {\n\t\t\tnew Car(\"Morris\", \"DD-AB-123\", 1);\n\t\t}).isInstanceOf(ConstraintViolationsException.class).satisfies(e -\u003e {\n\t\t\tConstraintViolations violations = ((ConstraintViolationsException) e).violations();\n\t\t\tassertThat(violations.isValid()).isFalse();\n\t\t\tassertThat(violations).hasSize(1);\n\t\t\tassertThat(violations.get(0).message()).isEqualTo(\"\\\"seatCount\\\" must be greater than or equal to 2\");\n\t\t});\n\t}\n\n\t@Test\n\tvoid carIsValid() {\n\t\tassertThatCode(() -\u003e {\n\t\t\tnew Car(\"Morris\", \"DD-AB-123\", 2);\n\t\t}).doesNotThrowAnyException();\n\t}\n\n}\n```\n\n#### Reusable and composable validators\n\nLet's take a look at a more advanced usage.\n\nThe following constraints are domain rules that can potentially be used not only for the `Car` class:\n\n\u003e * `manufacturer` must never be null\n\u003e * `licensePlate` must never be null and must be between 2 and 14 characters long\n\u003e * `seatCount` must be at least 2\n\nIn YAVI, you can define small validators for each value and then combine them to compose validators for objects.\n\nPlease take a look at the following definition:\n\n```java\npackage com.example;\n\nimport am.ik.yavi.builder.IntegerValidatorBuilder;\nimport am.ik.yavi.builder.StringValidatorBuilder;\nimport am.ik.yavi.core.Validated;\n\npublic final class Car {\n\n  public static StringValidator\u003cString\u003e manufacturerValidator = StringValidatorBuilder\n          .of(\"manufacturer\", c -\u003e c.notNull())\n          .build();\n\n  public static StringValidator\u003cString\u003e licensePlateValidator = StringValidatorBuilder\n          .of(\"licensePlate\", c -\u003e c.notNull().greaterThanOrEqual(2).lessThanOrEqual(14))\n          .build();\n\n  public static IntegerValidator\u003cInteger\u003e seatCountValidator = IntegerValidatorBuilder\n          .of(\"seatCount\", c -\u003e c.greaterThanOrEqual(2))\n          .build();\n\n  private static Arguments3Validator\u003cString, String, Integer, Car\u003e validator = manufacturerValidator\n          .split(licensePlateValidator)\n          .split(seatCountValidator)\n          .apply(Car::new);\n\n  private final String manufacturer;\n\n  private final String licensePlate;\n\n  private final Integer seatCount;\n\n  public static Validated\u003cCar\u003e of(String manufacturer, String licensePlate, Integer seatCount) {\n    return validator.validate(manufacturer, licensePlate, seatCount);\n  }\n\n  // omitted\n\n}\n```\n\nAfter defining validators for `manufacturer`, `licensePlate`, and `seatCount`, it combines these three validators (`StringValidator\u003cT\u003e` is a validator that validates a `String` and returns an object of type `T`, and `IntegerValidator\u003cT\u003e` is a validator that validates an `Integer` and returns an object of type `T`) to create an `Arguments3Validator\u003cString, String, Integer, Car\u003e`.\n\nThese small validators can also be used for generating other objects, which prevents domain rules from being scattered throughout the code.\n\n#### Where to go next?\n\nThat concludes the 5 minutes tour through the world of YAVI. If you want a more complete\nintroduction, it is recommended to read \"[Using YAVI](https://yavi.ik.am/#using-yavi)\" in the reference document.\n\n### Required\n\n* Java 8+\n\n### License\n\nLicensed under the Apache License, Version 2.0.\n","funding_links":[],"categories":["Java","\u003ca name=\"Java\"\u003e\u003c/a\u003eJava"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaking%2Fyavi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmaking%2Fyavi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaking%2Fyavi/lists"}