{"id":36417042,"url":"https://github.com/callius/target-kt","last_synced_at":"2026-01-11T17:00:11.812Z","repository":{"id":63708940,"uuid":"569901166","full_name":"callius/target-kt","owner":"callius","description":"Target - Functional domain modeling in Kotlin","archived":false,"fork":false,"pushed_at":"2025-10-01T04:44:37.000Z","size":258,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-01T04:57:23.498Z","etag":null,"topics":["annotation-processor","domain-driven-design","domain-model","domain-modeling","functional-data-structure","functional-programming","kotlin","kotlin-libraries","kotlin-library","value-object"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","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/callius.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","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":"2022-11-23T21:59:31.000Z","updated_at":"2025-10-01T04:43:31.000Z","dependencies_parsed_at":"2024-11-16T00:32:31.101Z","dependency_job_id":null,"html_url":"https://github.com/callius/target-kt","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/callius/target-kt","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/callius%2Ftarget-kt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/callius%2Ftarget-kt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/callius%2Ftarget-kt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/callius%2Ftarget-kt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/callius","download_url":"https://codeload.github.com/callius/target-kt/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/callius%2Ftarget-kt/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28314253,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-11T14:58:17.114Z","status":"ssl_error","status_checked_at":"2026-01-11T14:55:53.580Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["annotation-processor","domain-driven-design","domain-model","domain-modeling","functional-data-structure","functional-programming","kotlin","kotlin-libraries","kotlin-library","value-object"],"created_at":"2026-01-11T17:00:10.170Z","updated_at":"2026-01-11T17:00:11.717Z","avatar_url":"https://github.com/callius.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Target\n\nTarget is a library for Functional Domain Modeling in Kotlin, inspired by [arrow-kt](https://arrow-kt.io).\n\nTarget aims to provide a set of tools across all Kotlin platforms to empower users to quickly write pure, functionally\nvalidated domain models. For this, it includes a set of atomic components: `ValueFailure`, `ValueObject`,\nand `ValueValidator`. These components can be used on their own, or in conjunction with the\nincluded [KSP](https://kotlinlang.org/docs/ksp-overview.html) annotation processor.\n\n## Getting Started\n\n#### Value Failure\n\nA `ValueFailure` is an interface representing a failure during value validation.\n\n```kotlin\ninterface ValueFailure\u003cT\u003e {\n    val failedValue: T\n}\n```\n\n#### Value Object\n\nA `ValueObject` is an interface representing a validated value. By convention, value object implementations have a\nprivate primary constructor, so that they are not instantiated outside a `ValueValidator`. A value object implementation\nmust declare a companion object implementing a value validator when used in conjunction with the annotation processor\nlibrary.\n\n```kotlin\ninterface ValueObject\u003cT\u003e {\n    val value: T\n}\n```\n\n#### Value Validator\n\nA `ValueValidator` is an interface defining value validation functions. The primary validation function, `of`, takes an\ninput and returns either a `ValueFailure` or a `ValueObject`. By convention, a value validator implementation is an\nabstract class, because the value object's private constructor is often passed to its primary constructor as a\nreference.\n\n```kotlin\ninterface ValueValidator\u003cI, F : ValueFailure\u003cI\u003e, T : ValueObject\u003cI\u003e\u003e {\n\n    fun of(input: I): Either\u003cF, T\u003e\n\n    // ...\n}\n```\n\n### Examples\n\nThe included `StringInRegexValidator` class is an example of a `ValueValidator` implementation.\n\n```kotlin\nabstract class StringInRegexValidator\u003cT : ValueObject\u003cString\u003e\u003e(private val ctor: (String) -\u003e T) :\n    ValueValidator\u003cString, GenericValueFailure\u003cString\u003e, T\u003e {\n\n    protected abstract val regex: Regex\n\n    override fun of(input: String): Either\u003cGenericValueFailure\u003cString\u003e, T\u003e {\n        return if (regex.matches(input)) {\n            Either.Right(ctor(input))\n        } else {\n            Either.Left(GenericValueFailure(input))\n        }\n    }\n}\n```\n\nValue object classes can be inlined on the JVM. This `EmailAddress` class is an example of such a `ValueObject`\nimplementation.\n\n```kotlin\n/**\n * A W3C HTML5 email address.\n */\n@JvmInline\nvalue class EmailAddress private constructor(override val value: String) : ValueObject\u003cString\u003e {\n\n    companion object : EmailAddressValidator\u003cEmailAddress\u003e(::EmailAddress)\n}\n```\n\nThis value object can then be used to validate an email address like so:\n\n```kotlin\nsuspend fun createUser(params: UserParamsDto) = either {\n    val emailAddress = EmailAddress.of(params.emailAddress).bind()\n    // ... validating other params ...\n    repositoryCreate(\n        UserParams(\n            emailAddress = emailAddress\n            // ... passing other validated params ...\n        )\n    ).bind()\n}\n```\n\n## Annotation Processor\n\nThe Target annotation processor library makes it easy to create functionally validated models. It takes the fields\nof a model data class and generates:\n\n1. A sealed set of failure classes.\n2. A validation function `Model.Companion.of()` using said failure classes.\n3. A syntactic sugar function `Model.Companion.only()` when the model contains one or more fields with an `Option` type.\n\n### Failure\n\nThe failure class is a sealed interface containing data classes for each value object property declared on the model\ntemplate, containing a single value, `parent`, with a type of the value object validator's failure type.\n\n```kotlin\nsealed interface ModelFieldFailure {\n\n    data class Property1(val parent: Property1Failure) : ModelFieldFailure\n\n    data class Property2(val parent: Property2Failure) : ModelFieldFailure\n\n   // ...\n}\n```\n\n### Validation Function\n\nThe validation function, named `of`, validates the model's fields similar to the behavior of a `ValueValidator` by\ntaking the raw value object field types and performing cumulative validation, calling each value object's validator\nand returning either a non-empty list of model field failures or a model instance.\n\n```kotlin\nfun Model.Companion.of(/* arguments with raw field types */): Either\u003cNel\u003cModelFieldFailure\u003e, Model\u003e\n```\n\n### Optional Properties\n\nIt is also capable of validating optional value objects. This is useful when defining a model builder/update parameters\nclass representing updated model fields.\n\nIn addition to validating optional fields, the annotation processor will generate another function, named `only`, for\npartial instantiation, applying a default of `None` to each of those fields. This is useful for only updating some\nfields of a model without explicitly setting all others to `None`.\n\nHere's a minimal example:\n\n```kotlin\n/**\n * Model builder used to update a model.\n */\n@Validatable\ndata class ModelBuilder(\n    val property1: Option\u003cModelProperty1\u003e\n) {\n    companion object\n}\n\n/**\n * Validation function generated by the processor.\n */\nfun ModelBuilder.Companion.of(\n    property1: Option\u003cRawModelProperty1\u003e\n): Either\u003cNel\u003cModelBuilderFieldFailure\u003e, ModelBuilder\u003e {\n    TODO(\"...generated validation logic...\")\n}\n\n/**\n * Syntactic function generated by the processor.\n */\nfun ModelBuilder.Companion.only(\n    property1: Option\u003cModelProperty1\u003e = None\n): ModelBuilder = ModelBuilder(property1)\n\n/**\n * Function snippet of a usage example.\n */\nfun updateModelProperty1(repository: ModelRepository, id: ModelId, property1: ModelProperty1) {\n    repository.updateById(\n        id = id,\n        builder = ModelBuilder.only(\n            property1 = property1.some()\n            // ... all other builder properties will be set to None.\n        )\n    )\n}\n```\n\n### Nested Models\n\nNested models are a developing feature. A nested model field is defined just like any other field, with the type\nof its model data class. Its definition in the validation function will be as follows:\n\n```kotlin\n@Validatable\ndata class Model(\n   val child: ChildModel\n) {\n   companion object\n}\n\nfun Model.Companion.of(\n   child: Either\u003cNel\u003cChildModelFieldFailure\u003e, ChildModel\u003e\n) {\n   TODO()\n}\n```\n\nThis delegates the validation of the model to the models own validation function. A failure for it will also be\ngenerated for the parent model:\n\n```kotlin\nsealed interface ModelFieldFailure {\n\n   data class Child(val parent: Nel\u003cChildModelFieldFailure\u003e) : ModelFieldFailure\n}\n```\n\n### Usage Example\n\nDefine a model data class:\n\n```kotlin\n@Validatable\ndata class User(\n    val id: PositiveInt,\n    val firstName: FirstName,\n    val lastName: LastName,\n    val username: Username?,\n    val emailAddress: EmailAddress,\n    val phoneNumber: UserPhoneNumber?,\n    val updated: Instant,\n    val created: Instant\n) {\n    companion object\n}\n\n@Validatable\ndata class UserPhoneNumber(\n    val userId: PositiveInt,\n    val number: PhoneNumber,\n    val validated: Boolean,\n    val updated: Instant,\n    val created: Instant\n) {\n    companion object\n}\n\n@Validatable\ndata class UserParams(\n    val firstName: FirstName,\n    val lastName: LastName,\n    val username: Username?,\n    val emailAddress: EmailAddress,\n    val phoneNumber: UserPhoneNumberParams?\n) {\n    companion object\n}\n\n@Validatable\ndata class UserPhoneNumberParams(\n    val number: PhoneNumber,\n    val validated: Boolean\n) {\n    companion object\n}\n\n@Validatable\ndata class UserBuilder(\n    val firstName: Option\u003cFirstName\u003e,\n    val lastName: Option\u003cLastName\u003e,\n    val username: Option\u003cUsername?\u003e,\n    val emailAddress: Option\u003cEmailAddress\u003e,\n    val phoneNumber: Option\u003cUserPhoneNumberBuilder?\u003e\n) {\n    companion object\n}\n\n@Validatable\ndata class UserPhoneNumberBuilder(\n   val number: Option\u003cPhoneNumber\u003e,\n   val validated: Option\u003cBoolean\u003e\n) {\n   companion object\n}\n```\n\nRun a build and use the generated validation functions:\n\n```kotlin\nfun createUser() = either {\n   repository.create(\n      UserParams.of(\n          firstName = \"John\",\n          lastName = \"Doe\",\n          username = \"john.doe\",\n          emailAddress = \"john.doe@example.com\",\n          phoneNumber = UserPhoneNumberParams.of(\n              number = \"+11231231234\",\n              validated = false\n          )\n      ).bind()\n   ).bind()\n}\n\nfun greetUser(user: User) {\n    println(\"Hello, ${user.firstName.value}!\")\n    println(\"Your account was created on ${user.created}.\")\n}\n\nfun textUser(user: User, message: SmsTextMessage) = either {\n    ensureNotNull(user.phoneNumber) { NoPhoneNumber }.run {\n        ensure(validated) { NotValidated }\n        sendSms(number, message).bind()\n    }\n}\n\nfun updateUser(id: PositiveInt) = repository.update(\n    id,\n    UserBuilder.only(\n        username = null.some(),\n        phoneNumber = UserPhoneNumberBuilder.only(\n            validated = true.some()\n        ).some()\n    )\n)\n```\n\n## Gradle Setup\n\n\u003e Note that these libraries are experimental, and their APIs are subject to change.\n\n#### Target Core\n\n```kotlin\ndependencies {\n    implementation(\"io.target-kt:target-core:$targetVersion\")\n}\n```\n\n#### Target Core + Annotation Processor\n\n```kotlin\nplugins {\n    id(\"com.google.devtools.ksp\") version kspVersion\n}\n\ndependencies {\n    implementation(\"io.target-kt:target-core:$targetVersion\")\n    compileOnly(\"io.target-kt:target-annotation:$targetVersion\")\n    ksp(\"io.target-kt:target-annotation-processor:$targetVersion\")\n}\n```\n\nSee the [KSP docs](https://kotlinlang.org/docs/ksp-overview.html) for additional configuration details.\n\n## Roadmap\n\n1. Add `Parseable` annotation.\n    * Add `ValueObjectParser` interface.\n    * Generate `Model.Companion.parse()` function.\n2. Convert to compiler plugin and remove the need for `companion object` stubs once a compiler plugin API is released.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcallius%2Ftarget-kt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcallius%2Ftarget-kt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcallius%2Ftarget-kt/lists"}