Journal3 isn't packed with groundbreaking features; it's just another journaling app. But what sets it apart, at least in my view, is its role as a thought experiment—a space to ponder the "what-ifs."
What if we could build an Android app without leaning on a specific framework, relying on tried-and-true software engineering patterns and object-oriented principles instead?
What if we could bring the power and adaptability of declarative programming into the domain?
What if we started treating objects not just as data containers, but as entities capable of intelligent actions?
Each feature (e.g., editing a moment, selecting a place) has a corresponding UseCase
class modeling the interactions between the user and the system. These UseCase
classes serve as the deepest core and do not return values to the caller.
A UseCase
typically depends on an EventSource
that exposes a stream of Event
s. For example, UseCases
related to editing may attempt to update the underlying model upon observing a text input event. These Event
s can originate from user actions like clicks, text inputs, and Back button taps, as well as from the system (e.g., Activity lifecycle changes).
After processing, Event
s are then sunk into EventSink
s. These sinks can be either global or local. Global sinks provide complete observability to the entire domain, enabling functionalities like application-wide logging. Local sinks, on the other hand, may serve scoped functionalities such as finishing the Activity upon receiving terminal events.
The primary role of a UseCase
is to act as an orchestrator. For example, if a feature involves persisting a user's change, the UseCase
will not know how to perform the action itself. Instead, it will delegate the responsibility to a model. It is then the model's responsibility to actually complete the task at hand.
This same principle applies to presentation. If a feature involves displaying a list of items sourced remotely (e.g., from a web API) to the user, it depends on appropriate models to load the data and render it on the screen.
- Full encapsulation of business requirements within
UseCases
makes writing tests for them a breeze. - The heavy reliance on interfaces allows for composable behaviors, promoting flexibility.
- Loosely-typed message passing enables
Events
not only to drive interactions but also to serve as a means of observability. - The sole purpose of Activities is to declare a
UseCase
and invoke it, keeping them light and concise.
Are you interested in joining this journey with me? Feel free to open issues to initiate discussions about anything related to Journal3. I also welcome any kind of pull requests that can help advance this project.
- Journal3 currently relies on several external services such as HERE Maps, OpenAI, and Sentry. If you plan to build the project on your machine, you may need to provide your own keys for these services.
- Please note that formatting and branch coverage are strictly enforced in the CI pipeline. Ensure that you've run
./gradlew check
before submitting a pull request.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.