Skip to content
datus enables you to define a conversion process between two data structures in a fluent functional API
Java
Branch: master
Clone or download
Pull request Compare This branch is 2 commits behind roookeee:master.
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
sample-projects
src
.gitignore
LICENSE
README.md
USAGE.md
pom.xml

README.md

datus enables you to define a conversion process between two data structures in a fluent functional API. Define conditional processing, do some business relevant work or just pass values from a to b - just about everything is possible for both mutable and immutable data structures.

Replace your conversion factories/helpers with datus and focus on what to map, not how: define your conversion, eliminate the need to test the conversion aspects of your application and focus on what really matters - your project.

Overview

  1. Why use datus?
  2. Examples
  3. Sample projects
  4. User guide
  5. Dependency information (Maven etc.)
  6. Development principles
  7. Supporting datus development

Why use datus?

Using datus has the following benefits:

  • separation of concerns: write your mapping in datus while using the business logic of other components (no more monolithic classes that do both)
  • reducing dependencies: enable your businesslogic to operate on parts of a data structure instead of depending on the whole object (e.g. upper casing a persons name in a .map-step)
  • cleaner abstractions: programming against an interface instead of concrete classes
  • declarative/functional-programming approach: focus on what to map, not how to do it
  • simplicity: compared to Lombok and Apache MapStruct no additional IDE or build system plugins are needed
  • explicitness: no black magic - you define what to map and how (compile-time checked), not naming conventions, annotations or heuristics
  • low coupling: leave your POJO/data classes 'as is' - no need for annotations or any other modifications
  • less classes: no more 'dumb'/businesslogic less Factory-classes that you have to unit test
  • cross-cutting concerns: easily add logging (or other cross-cutting concerns) via spy or process(see below for more information about the full API)
  • rich mapping API: define the mapping process from A -> B and get Collection<A> -> Collection<B>, Collection<A> -> Map<A, B> and more for free
  • focus on meaningful unit tests: no need to unit test trivial but necessary logic (e.g. null checking, which once fixed won't be a problem at the given location again)
  • clarity: (subjectively) more self-documenting code when using datus mapping definitions
  • no reflection or code generation: datus implementation is just plain java, use GraalVM Native Image or any JVM out there - datus will always run out of the box without any further configuration or limitations

Examples

The following examples outline the general usage of both the immutable and mutable API of datus. Please refer to the USAGE.md for an extensive guide on datus that includes a fictional, more complex scenario with changing requirements.

Immutable object API example

class Person {
    //getters + constructor omitted for brevity
    private final String firstName;
    private final String lastName;
   
}

class PersonDTO {
    //getters omitted for brevity
    private final String firstName;
    private final String lastName;
    
    public PersonDTO(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
}

//define a building process for each constructor parameter, step by step
//the immutable API defines constructor parameters in their declaration order
Mapper<Person, PersonDTO> mapper = Datus.forTypes(Person.class, PersonDTO.class).immutable(PersonDTO::new)
    .from(Person::getFirstName).to(ConstructorParameter::bind)
    .from(Person::getLastName).nullsafe()
        .given(String::isEmpty, "fallback").orElse(ln -> ln.toUpperCase())
        .to(ConstructorParameter::bind)
    .build();
    
Person person = new Person();
person.setFirstName("firstName");
person.setLastName(null);
PersonDTO personDto = mapper.convert(person);
/*
    personDto = PersonDTO [
        firstName = "firstName",
        lastName = null
    ]
*/
person.setLastName("");
personDto = mapper.convert(person);
/*
    personDto = PersonDTO [
        firstName = "firstName",
        lastName = "fallback"
    ]
*/
person.setLastName("lastName");
personDto = mapper.convert(person);
/*
    personDto = PersonDTO [
        firstName = "firstName",
        lastName = "LASTNAME"
    ]
*/

Mutable object API example

class Person {
    //getters + setters omitted for brevity
    private String firstName;
    private String lastName;
}

class PersonDTO {
    //getters + setters + empty constructor omitted for brevity
    private String firstName;
    private String lastName;
}

//the mutable API defines a mapping process by multiple getter-setter steps
Mapper<Person, PersonDTO> mapper = Datus.forTypes(Person.class, PersonDTO.class).mutable(PersonDTO::new)
    .from(Person::getFirstName).into(PersonDTO.setFirstName)
    .from(Person::getLastName).nullsafe()
        .given(String::isEmpty, "fallback").orElse(ln -> ln.toUpperCase())
        .into(PersonDTO::setLastName)
    .from(/*...*/).into(/*...*/)
    .build();
    
Person person = new Person();
person.setFirstName("firstName");
person.setLastName(null);
PersonDTO personDto = mapper.convert(person);
/*
    personDto = PersonDTO [
        firstName = "firstName",
        lastName = null
    ]
*/
person.setLastName("");
personDto = mapper.convert(person);
/*
    personDto = PersonDTO [
        firstName = "firstName",
        lastName = "fallback"
    ]
*/
person.setLastName("lastName");
personDto = mapper.convert(person);
/*
    personDto = PersonDTO [
        firstName = "firstName",
        lastName = "LASTNAME"
    ]
*/

Sample projects

There are two sample projects located in the sample-projects directory that showcase most of datus features in two environments: framework-less and with Spring Boot.

Hop right in and tinker around with datus in a compiling environment!

User guide

Please refer to the USAGE.md for a complete user guide as the readme only serves as a broad overview. The user guide is designed to take at most 15 minutes to get you covered on everything about datus and how to use it in different scenarios.

Dependency information

Maven

<dependency>
  <groupId>com.github.roookeee</groupId>
  <artifactId>datus</artifactId>
  <version>1.3.0</version>
</dependency>

or any other build system via Maven Central

Maven Central

Development principles

This section is about the core principles datus is developed with.

Mutation testing

datus uses pitest to secure the quality of all implemented tests and has no surviving mutations outside of Datus helper functions (which only aid type inference and are thus not tested) and some constructors of the immutable API that only delegate and are thus not explicitly tested.

Branching

The master branch always matches the latest release of datus while the develop branch houses the next version of datus that is still under development.

Semver

datus follows semantic versioning (see https://semver.org/) starting with the 1.0 release.

Licensing

datus is licensed under The MIT License (MIT)

Copyright (c) 2019 Nico Heller

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Supporting datus development

Like datus ? Consider buying me a coffee :)

Buy Me A Coffee

You can’t perform that action at this time.