All Projects â†’ brianium â†’ clean-todos

brianium / clean-todos

Licence: other
📋 A todo example leveraging clean architecture in Clojure

Programming Languages

clojure
4091 projects
CSS
56736 projects
HTML
75241 projects

Projects that are alternatives of or similar to clean-todos

Go-gRPC-RabbitMQ-microservice
Go gRPC RabbitMQ email microservice
Stars: ✭ 107 (+42.67%)
Mutual labels:  clean-architecture
movies
An example approach for modularization, reactive clean architecture and persistancy.
Stars: ✭ 110 (+46.67%)
Mutual labels:  clean-architecture
CleanArchitectureDemo
This is a demo project built on .NET Core 3.1 Clean Architecture. Please refer the articles mentioned in the readme to learn more.
Stars: ✭ 33 (-56%)
Mutual labels:  clean-architecture
flutter clean architecture
A flutter clean architecture series, the way we build clean apps.
Stars: ✭ 225 (+200%)
Mutual labels:  clean-architecture
re-alm
An Elm Architecture experiment in ClojureScript
Stars: ✭ 24 (-68%)
Mutual labels:  re-frame
WWDCast
The unofficial WWDC application for iOS
Stars: ✭ 22 (-70.67%)
Mutual labels:  clean-architecture
Transact
A transaction management android app which allows you to verify records from both parties before addition.
Stars: ✭ 21 (-72%)
Mutual labels:  clean-architecture
NodeJS-A-Clean-Archticture-NJCA
This is a simple NodeJS Architecture can be used in an enterprise applications. 🎉🤔
Stars: ✭ 64 (-14.67%)
Mutual labels:  clean-architecture
CleanArchitecture
Android App Architecture
Stars: ✭ 14 (-81.33%)
Mutual labels:  clean-architecture
fastapi-pydiator
Python clean architecture and usecase implementation with fastapi and pydiator-core
Stars: ✭ 58 (-22.67%)
Mutual labels:  clean-architecture
GithubApp-android-architecture
Let's learn a deep look at the Android architecture
Stars: ✭ 16 (-78.67%)
Mutual labels:  clean-architecture
CleanTesting
Sample code for the Clean Testing talk.
Stars: ✭ 72 (-4%)
Mutual labels:  clean-architecture
Stars
An android application build with a clean architecture approach and Star wars API
Stars: ✭ 54 (-28%)
Mutual labels:  clean-architecture
nodejs-clean
Clean Architecture with Node.js + Express.js
Stars: ✭ 136 (+81.33%)
Mutual labels:  clean-architecture
BESTV
Android TV App powered by TMDb. It is a easy way to find the best TV content, the top movies, series... all of that in your TV.
Stars: ✭ 49 (-34.67%)
Mutual labels:  clean-architecture
Android-Clean-Architecture
🚀A basic sample android application to understand Clean Architecture in a very simple way and is written in Kotlin.
Stars: ✭ 39 (-48%)
Mutual labels:  clean-architecture
pojo-observer
A minimalist object observer with React hooks support. Allows you to separate concerns between presentation and interaction logic
Stars: ✭ 91 (+21.33%)
Mutual labels:  clean-architecture
go-todo-app
Go + Angular Todo App
Stars: ✭ 21 (-72%)
Mutual labels:  clean-architecture
re-frame-routing
ClojureScript (re-frame) library that manages routing and route state.
Stars: ✭ 16 (-78.67%)
Mutual labels:  re-frame
social-me-die
A social media experiment with .net core, ef core, react and mobx.
Stars: ✭ 16 (-78.67%)
Mutual labels:  clean-architecture

Clean Todos

An example of clean architecture in Clojure. The goal of this project is to demonstrate the ever elusive clean architecture in Clojure, and also serve as a gentle introduction to clojure.spec (for me and hopefully others).

Running the various deliveries should be a snap once you get leiningen installed.

If you are interested - please see the retrospect. One can only learn so much from the classic "todo" app, but I feel this app has given me a better understanding of the Clojure(Script) landscape as a whole.

The original version of this application inspired the yoose library - a library that facilitates use case centered applications. The current version of this app leverages yoose.

Delivery (i.e presentation for the app)

The todos.delivery namespace contains different delivery mechanisms for clean todos - i.e cli, web, etc..

todos.delivery.cli

This delivery provides a command line app for managing todos. Todos are persisted using sqlite.

Usage

Usage:
  todos command [options]

Options:
  -s, --status STATUS  all  Todo status to filter on
  -h, --help

Available Commands:
  create: Create a new todo
  create todo-name

  list: List todos
  list --status=STATUS <completed,active,all>

  toggle: Toggle todo status
  toggle todo-id

  delete: Permanently removes a todo
  delete todo-id

Building

This delivery is built to a single executble named todos using the lein binplus plugin. Lein can then be used to build the cli delivery using the cli profile:

$ lein with-profile cli bin

todos.delivery.api

The api delivery provides a simple restful interface for managing todos.

The following routes are supported:

GET    /todos{?status=completed,active} - status defaults to all todos
POST   /todos
DELETE /todos{/id}
PATCH  /todos{/id}

POST expects a JSON document of the form {"title": "string", "complete?": boolean}

PATCH expects a similar document to POST with the difference that all keys are optional.

All inputs are validated via clojure.spec.alpha/conform.

Running the server

The server is powered via lein-ring. Just include the api profile when running:

$ lein api

todos.delivery.web

The web delivery is a re-frame app that leverages the api delivery.

Running the web application

The web app is run in a dev setting using lein-figwheel. The api delivery needs to be running in order to use the web delivery.

I'm not a lein wizard so I wasn't able to figure out how to run both in a single command (at least in a sane way) - so in order to demo the web app first start the api server in a terminal session:

$ lein api

Then in another terminal session start the web app:

$ lein web

You should then we able to visit http://localhost:3449 to see the re-frame app in action.

Testing

This application leverages a mixture of traditional unit tests and generative testing via clojure.test.check. It's pretty dern cool, so check out the test suite. Unit tests and generative tests can be run at once via:

$ lein test

Retrospect

I'm not sure if this approach is a purist approach to the clean architecture. Everything is very use case driven, and the architecture should make it clear what the intent of the application is. The concept of input and output ports is handled (cleanly in my opinion) by core.async channels. Dependency injection is handled (again cleanly in my opinion) by mount.

I don't have much experience building applications in Clojure(Script) - but I have leveraged similar concepts in larger apps using different languages. While a todo app can only teach so much - I was just floored by the elegance of Clojure(Script) in building a use case centered app with different deliveries.

Basic Architecture

The architecture lays out like so:

resources/ -- static assets leveraged by web delivery
src/
  todos/
    core/ -- contains use cases, entities, and some simple conventions for messaging
      action/
      entity/
      use_case/
        create_todo/
	delete_todo/
	list_todos/
	update_todo/
    delivery/
      api/ -- a simple restful api
      cli/ -- a command-line interface to the app
      web/ -- a re-frame application
    storage/
      todo/ -- protocol implementations for persisting todos

On core.async

The concept of inputs and outputs in the clean architecture just seemed to make sense as core.async channels. While this made sense to me - I'm no expert and I'm not sure I fully understand the implications of this choice. I do know that having a use case depend on channels opens the door for tons of flexibility - i.e (one-to-many channels, controlling buffers, etc..). The api is also fairly easy to use.

The deliveries I created usually leveraged a put followed by a blocking take - mostly out of necessity (blocking in the cli for example). An async take would be pretty slick for streaming interfaces.

On clojure.spec

clojure.spec is amazing. I'm likely not leveraging it to the best of it's ability. The cool things I saw were:

  • Generative testing via test.check. My functions are pretty thoroughly tested by a variety of inputs.
  • Validation. The api and web deliveries use specs to validate inputs and outputs. This is especially cool in the re-frame app as it checks the shape of app state after every mutation. This turned up all kinds of errors during development.
  • Documentation. Specs do a great job of documenting intent

Some things that I struggled with a bit - but might have the beginning of understanding:

  • Generators. Making custom generators took a bit of legwork to understand and employ
  • Convention - still not sure if specs make sense in the same file or a different one from the code they are describing. I ultimately settled on different files. It creates a little bloat - but makes sense and they are easy to import when composing other specs.

On ClojureScript

The web delivery leverages re-frame. This choice was largely due to my interest in the framework. I spend a good chunk of my day-to-day writing React/Redux apps using ES6/Next.

I personally prefer ClojureScript over vanilla JavaScript. The tooling, the standard library, persistent collections out of the box.. You can just do more with less. lein-figwheel solves a lot of the problems that webpack does, and in some ways I find it easier to use.

The web delivery may fall out of the clean architecture camp because it did not actually use any of the use cases. While it is structured by use cases - actually leveraging the use case namespaces felt awkward. Re-frame solves a lot of the input/output problems itself.

What I did get to re-use: was my specs and core application code - thanks to the wonder of reader conditionals and .cljc files. This was nearly effortless, and it just blows my mind. Writing Clojure that works on the server and the client. Wow.

On code organization

Not sure where I landed on this. The current code base is one repo supporting all deliveries. That means it is a mixture of .clj, .cljs, and .cljc files. At the time of this writing - documentation on the subject isn't terribly clear on convention. The points made in favor of separate src/clj, src/cljs, and src/cljc directories are pretty compelling, and I think I would opt for this approach in the next app I build.

The benefit of one repo with many deliveries is still unclear to me. It wasn't terribly difficult, but I didn't do any of the work of deploying all deliveries. I'm not sure on the nuances of lein when it comes to building multiple jars with different requirements. Something to learn still! Basically comes down to the question of one project.clj or multiple?

The lein-parent plugin seems like it offers some conventions for managing dependencies and multiple projects in a mono repo context.

Todos (snicker)

The problem space of todos provides a finite amount of interest for me. I left some things out, and there are some other things that I think would be cool. I may or may not ever get to any of these.

  • A re-natal delivery. It would be cool to see a delivery for a native mobile app
  • Error handling in the web delivery. Like a good "RUSH TO MARKET APP" the web delivery just assumes all http requests will always succeed.
  • Document more of this

Feedback / Pull Requests

I would welcome any pull requests or general feedback on any approach taken here. I'm not sure I ever have or ever will fully grasp what a computer is, so I always welcome people telling me how to do things better :)

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].