Skip to content

arkitik/radix

Repository files navigation

Contributors Forks Stargazers Issues MIT License


Logo

Radix

Provide the needed infrastructure to build clean Domain-Driven Design (DDD) apps in the Java ecosystem
Explore the docs »

Report Bug · Request Feature

Table of Contents
  1. About The Project
  2. Getting Started
  3. Usage
  4. Roadmap
  5. Contributing
  6. License
  7. Contact
  8. Built With

About The Project

Radix is a lightweight Kotlin framework that provides a blueprint to build clean, easy-to-maintain, and highly-scalable server-side components. It emphasizes on the idea of isolating the bussiness logic of the app from 3rd party dependencies (e.g. Spring) by keeping all core business logic in the usecases layer which makes it easy to expose different kind of services and even port the app into a different framework.

(back to top)

Getting Started

Add the below dependency to the project's dependency management:

<dependency>
    <groupId>io.arkitik.radix</groupId>
    <artifactId>radix-dependencies</artifactId>
    <version>v1.6.0</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>

Prerequisites

  • JDK version >= 1.8

Usage

Let's build a simple User domain! To do so, we should start with the abstraction from the identity level and then move to the implementation.

1. Identity:

The identity in simple terms is the abstraction of the entity, it extends Identity<ID : Serializable> and specifies the value of the generic type ID.

interface User : Identity<String> {
    override val uuid: String
    val username: String
    val password: String
}

→ it's available through the below dependency:

<dependency>
    <groupId>io.arkitik.radix</groupId>
    <artifactId>radix-development-identity</artifactId>
</dependency>

2. Store:

The store is similar to the Data Access Object (DAO) pattern, it allows us to isolate the business layer from the persistence layer which means you can swap different persistence layers or change the implementation without affecting the core bussiness of the app! Aside from that, it also provides builders to create/update the entity.

To create our own UserStore we should extend from Store<ID : Serializable, I : Identity<ID>>

interface UserStore : Store<String, UserIdentity> {
    override fun UserIdentity.save(): UserIdentity
    override val storeQuery: StoreQuery<String, UserIdentity>
    override fun identityCreator(): StoreIdentityCreator<String, UserIdentity>
    override fun UserIdentity.identityUpdater(): StoreIdentityUpdater<String, UserIdentity>
}

→ it's available through the below dependency:

<dependency>
    <groupId>io.arkitik.radix</groupId>
    <artifactId>radix-development-store</artifactId>
</dependency>

3. Usecase Factory:

The usecase factory allows the consumer to create usecase objects without having to know the details about their implementation, dependencies, or how they get created.

As a consumer, all you need to know is the signature of the usecase which is basically:

  1. What does the usecase do (interpreted from the usecase name).
  2. The usecase request.
  3. The usecase response.

Let's create our own UserUsecaseFactory and add findUserByUuidUseCase usecase to it:

interface UserUseCaseFactory : UseCaseFactory {
    val findUserByUuidUseCase: FunctionalUseCase<FindUserRequest, FindUserResponse>
}

and the request/response as follows:

data class FindUserRequest(val uuid: String) : UseCaseRequest

data class FindUserResponse(val identity: UserIdentity?) : UseCaseResponse

→ it's available through the below dependency:

<dependency>
    <groupId>io.arkitik.radix</groupId>
    <artifactId>radix-development-usecase</artifactId>
</dependency>

4. Usecase:

The usecase layer is the brains of the app, the core business logic that your app needs to run and is defined through contract abstraction in the usecase-factory.

In our case, we have only one factory with one usecase which is findUserByUuidUseCase, to implement it we have to inject the StoreQuery available from the UserStore.

class FindUserByUuidUseCase(
    private val storeQuery: StoreQuery<String, UserIdentity>,
) : FunctionalUseCase<FindUserRequest, FindUserResponse> {
    override fun FindUserRequest.process() = FindUserResponse(storeQuery.find(uuid))
}

and finally the usecase-factory implementation:

class UserUseCaseFactoryImpl(
  userStore: UserStore,
) : BankUseCaseFactory {
  override val findUserByUuidUseCase = FindUserByUuidUseCase(userStore.storeQuery)
}

Of course, don't forget to provide the implementation for the identity (i.e. entity) and the store (i.e. adapter). For more examples, please refer to the Documentation.

(back to top)

Roadmap

  • Complete the Documentation files
    • Business Layer
    • Implementation Layer
    • Application Layer

See the open issues for a full list of proposed features (and known issues).

(back to top)

Contributing

Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.

If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement". Don't forget to give the project a star! Thanks again!

  1. Fork the Project
  2. Create your Feature Branch (git checkout -b feature/AmazingFeature)
  3. Commit your Changes (git commit -m 'Add some AmazingFeature')
  4. Push to the Branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

(back to top)

License

Distributed under the Apache License 2.0. See LICENSE for more information.

(back to top)

Contact

Ibrahim Al-Tamimi - ibm.iloom@gmail.com

Project Link: https://github.com/arkitik/radix

(back to top)

Built With

Kotlin Maven

(back to top)

About

Provide the needed infrastructure to build clean Domain-Driven Design (DDD) apps in the Java ecosystem

Resources

License

Stars

Watchers

Forks

Packages

No packages published