{"id":29981988,"url":"https://github.com/roookeee/datus","last_synced_at":"2025-09-06T05:40:58.428Z","repository":{"id":57722483,"uuid":"185678226","full_name":"roookeee/datus","owner":"roookeee","description":"datus enables you to define a conversion process between two data structures in a fluent functional API","archived":false,"fork":false,"pushed_at":"2023-09-22T09:51:37.000Z","size":221,"stargazers_count":41,"open_issues_count":1,"forks_count":5,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-08-03T18:50:52.893Z","etag":null,"topics":["conversion","datus","factory","factory-pattern","functional","java","java-8","mapping"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/roookeee.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-05-08T20:55:15.000Z","updated_at":"2024-09-08T12:14:45.000Z","dependencies_parsed_at":"2022-09-26T21:50:31.036Z","dependency_job_id":null,"html_url":"https://github.com/roookeee/datus","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/roookeee/datus","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/roookeee%2Fdatus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/roookeee%2Fdatus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/roookeee%2Fdatus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/roookeee%2Fdatus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/roookeee","download_url":"https://codeload.github.com/roookeee/datus/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/roookeee%2Fdatus/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268618527,"owners_count":24279249,"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","status":"online","status_checked_at":"2025-08-03T02:00:12.545Z","response_time":2577,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["conversion","datus","factory","factory-pattern","functional","java","java-8","mapping"],"created_at":"2025-08-04T17:03:44.578Z","updated_at":"2025-08-04T17:03:54.499Z","avatar_url":"https://github.com/roookeee.png","language":"Java","funding_links":["https://www.buymeacoffee.com/roookeee"],"categories":["Bean映射\u0026复制"],"sub_categories":[],"readme":"\u003cimg width=\"400px\" src=\"https://raw.githubusercontent.com/roookeee/datus/assets/assets/datus-logo.png\" \u003e\n\ndatus enables you to define a conversion process between two data structures in a fluent functional API.\nDefine conditional processing, do some business relevant work or just pass values from a to b - just about everything \nis possible for both mutable and immutable data structures.\n\nReplace your conversion factories/helpers with *datus* and focus on what to map, not how:\ndefine your conversion, eliminate the need to test the conversion aspects\nof your application and focus on what really matters - your project.\n\n#### Overview\n1. [Why use datus?](#why-use-datus)\n2. [Examples](#examples)\n3. [Sample projects](#sample-projects)\n4. [User guide](#user-guide)\n5. [Dependency information (Maven etc.)](#dependency-information)\n6. [Supported JVM languages](#supported-jvm-languages)\n6. [Development principles](#development-principles)\n7. [Supporting datus development](#supporting-datus-development)\n\n## Why use *datus*?\nUsing *datus* has the following benefits:\n\n- **separation of concerns**: write your mapping in *datus* while using the business logic of other components (no more monolithic classes that do both)\n- **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)\n- **cleaner abstractions**: programming against an `interface` instead of concrete classes\n- **declarative/functional-programming approach**: focus on `what` to map, not `how` to do it \n- **simplicity**: compared to [Lombok](https://projectlombok.org/) and [Apache MapStruct](http://mapstruct.org/) no additional IDE or build system plugins are needed\n- **explicitness**: no black magic - you define what to map and how (compile-time checked), not naming conventions, annotations or heuristics\n- **low coupling**: leave your POJO/data classes 'as is' - no need for annotations or any other modifications\n- **less classes**: no more 'dumb'/businesslogic less `Factory`-classes that you have to unit test\n- **cross-cutting concerns**: easily add logging (or other cross-cutting concerns) via `spy` or `process`(see below for more information about the full API)\n- **rich mapping API**: define the mapping process from `A -\u003e B` and get `Collection\u003cA\u003e -\u003e Collection\u003cB\u003e`, `Collection\u003cA\u003e -\u003e Map\u003cA, B\u003e` and more for free\n- **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)\n- **clarity**: (subjectively) more self-documenting code when using *datus* mapping definitions\n- **no reflection or code generation**: *datus* implementation is just plain java, \nuse [GraalVM Native Image](https://www.graalvm.org/docs/reference-manual/aot-compilation/) or any JVM out there -\n *datus* will always run out of the box without any further configuration or limitations\n\n## Examples\nThe following examples outline the general usage of both the immutable and mutable API of *datus*.\nPlease refer to the [USAGE.md](https://github.com/roookeee/datus/blob/master/USAGE.md) for an extensive guide on *datus*\nthat includes [a fictional, more complex scenario with changing requirements](https://github.com/roookeee/datus/blob/master/USAGE.md#examples).\n#### Immutable object API example\n```java\n\nclass Person {\n    //getters + constructor omitted for brevity\n    private final String firstName;\n    private final String lastName;\n   \n}\n\nclass PersonDTO {\n    //getters omitted for brevity\n    private final String firstName;\n    private final String lastName;\n    \n    public PersonDTO(String firstName, String lastName) {\n        this.firstName = firstName;\n        this.lastName = lastName;\n    }\n}\n\n//define a building process for each constructor parameter, step by step\n//the immutable API defines constructor parameters in their declaration order\nMapper\u003cPerson, PersonDTO\u003e mapper = Datus.forTypes(Person.class, PersonDTO.class).immutable(PersonDTO::new)\n    .from(Person::getFirstName).to(ConstructorParameter::bind)\n    .from(Person::getLastName).nullsafe()\n        .given(String::isEmpty, \"fallback\").orElse(ln -\u003e ln.toUpperCase())\n        .to(ConstructorParameter::bind)\n    .build();\n    \nPerson person = new Person();\nperson.setFirstName(\"firstName\");\nperson.setLastName(null);\nPersonDTO personDto = mapper.convert(person);\n/*\n    personDto = PersonDTO [\n        firstName = \"firstName\",\n        lastName = null\n    ]\n*/\nperson.setLastName(\"\");\npersonDto = mapper.convert(person);\n/*\n    personDto = PersonDTO [\n        firstName = \"firstName\",\n        lastName = \"fallback\"\n    ]\n*/\nperson.setLastName(\"lastName\");\npersonDto = mapper.convert(person);\n/*\n    personDto = PersonDTO [\n        firstName = \"firstName\",\n        lastName = \"LASTNAME\"\n    ]\n*/\n```\n\n#### Mutable object API example\n```java\nclass Person {\n    //getters + setters omitted for brevity\n    private String firstName;\n    private String lastName;\n}\n\nclass PersonDTO {\n    //getters + setters + empty constructor omitted for brevity\n    private String firstName;\n    private String lastName;\n}\n\n//the mutable API defines a mapping process by multiple getter-setter steps\nMapper\u003cPerson, PersonDTO\u003e mapper = Datus.forTypes(Person.class, PersonDTO.class).mutable(PersonDTO::new)\n    .from(Person::getFirstName).into(PersonDTO.setFirstName)\n    .from(Person::getLastName).nullsafe()\n        .given(String::isEmpty, \"fallback\").orElse(ln -\u003e ln.toUpperCase())\n        .into(PersonDTO::setLastName)\n    .from(/*...*/).into(/*...*/)\n    .build();\n    \nPerson person = new Person();\nperson.setFirstName(\"firstName\");\nperson.setLastName(null);\nPersonDTO personDto = mapper.convert(person);\n/*\n    personDto = PersonDTO [\n        firstName = \"firstName\",\n        lastName = null\n    ]\n*/\nperson.setLastName(\"\");\npersonDto = mapper.convert(person);\n/*\n    personDto = PersonDTO [\n        firstName = \"firstName\",\n        lastName = \"fallback\"\n    ]\n*/\nperson.setLastName(\"lastName\");\npersonDto = mapper.convert(person);\n/*\n    personDto = PersonDTO [\n        firstName = \"firstName\",\n        lastName = \"LASTNAME\"\n    ]\n*/\n```\n## Sample projects\nThere are two sample projects located in the [sample-projects](https://github.com/roookeee/datus/tree/master/sample-projects) directory\nthat showcase most of *datus* features in two environments: [framework-less](https://github.com/roookeee/datus/tree/master/sample-projects/plainjava)\n and [with Spring Boot](https://github.com/roookeee/datus/tree/master/sample-projects/samplespring).\n \nHop right in and tinker around with *datus* in a compiling environment!\n\n## User guide\nPlease refer to the [USAGE.md](https://github.com/roookeee/datus/blob/master/USAGE.md) for a complete user guide as the readme only serves as a broad overview.\nThe user guide is designed to take **at most 15 minutes** to get you covered on everything about *datus* and how to use it\nin different scenarios.\n\n## Dependency information\nMaven\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003ecom.github.roookeee\u003c/groupId\u003e\n  \u003cartifactId\u003edatus\u003c/artifactId\u003e\n  \u003cversion\u003e1.5.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\nor any other build system via Maven Central\n\n[![Maven Central](https://img.shields.io/maven-central/v/com.github.roookeee/datus.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22com.github.roookeee%22%20AND%20a:%22datus%22)\n\n## Supported JVM languages\n*datus* currently only supports Java as every other popular statically typed language on the JVM has fundamental issues with *datus*\ncore workflow. Non-statically typed languages like Clojure are not tested for compatability.\n\n##### Kotlin issues\nKotlin has a myriad of issues when it comes to the type-inference chain of *datus* builder-like API which makes 1:1 translations\nof Java to Kotlin (e.g. immutable class to data class) uncompilable. The encoding\nof (non-) nullability in Kotlins type system makes using method references with *datus* nearly impossible when using a nullable type\nand referencing Java functions from Kotlin (e.g. something simple as `Long::parseLong` on a `Long?`).\n\n##### Scala issues\nScala offers no way to obtain a method reference to a constructor of an object which forces *datus* users to provide\na lambda which delegates to the corresponding constructor. This lambda has to be fully typed as Scalas overload resolution\n/ type-inference cannot handle it otherwise. This makes using the immutable API of datus cumbersome which is especially\nbad for a language like Scala that is focused around immutability.\n\nThe aforementioned issues are not exhaustive and more issues are likely to arise when working around them.\nTherefor it is discouraged to use *datus* in any other language than Java right now.\n\n## Development principles \nThis section is about the core principles *datus* is developed with.\n\n#### Benchmarking\nEvery release of *datus* is monitored for performance regressions by a simple JMH suite that checks the core \nfunctionality of datus (simple mapping from `a-\u003eb`). \nSee `com.github.roookeee.datus.performance.PerformanceBenchmarkTest` for further insight.\n\n#### Mutation testing\n*datus* uses [pitest](http://pitest.org/) to secure the quality of all implemented tests and has no surviving mutations outside\nof `Datus` helper functions (which only aid type inference and are thus not tested), some constructors of the immutable API that only\ndelegate and are thus not explicitly tested and pitest edge-cases which are not testable / programmatically producible.\n\n#### Branching\nThe `master` branch always matches the latest release of *datus* while the `develop` branch houses the next version of datus\nthat is still under development.\n\n#### Semver\n*datus* follows semantic versioning (see https://semver.org/) starting with the 1.0 release.\n\n#### Licensing\n*datus* is licensed under The MIT License (MIT)\n\nCopyright (c) 2019-2023 Nico Heller\n\nPermission 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:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE 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.\n\n## Supporting datus development\nLike *datus* ? Consider buying me a coffee :)\n\n\u003ca href=\"https://www.buymeacoffee.com/roookeee\" target=\"_blank\"\u003e\u003cimg src=\"https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png\" alt=\"Buy Me A Coffee\" style=\"height: auto !important;width: auto !important;\" \u003e\u003c/a\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Froookeee%2Fdatus","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Froookeee%2Fdatus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Froookeee%2Fdatus/lists"}