Online version of the Lost Cities game built with Java, Spring Boot, and VueJS.
Adding Docker soon
First, clone the repository: git clone https://github.com/nathan815/LostCities
Requires JDK 11+ (javac -version
). Either OpenJDK or Oracle JDK.
cd backend
🛠 Compile: ./gradlew build
./gradlew bootRun
Requires NodeJS 12+ and npm (node package manager, included with NodeJS).
cd frontend
🛠 Install dependencies: npm install
npm run serve
⚡️ Production Build: npm run build
After running the serve command, frontend Webpack Dev Server should be up and running on http://localhost:8088. It is configured to automatically proxy API requests to localhost:8088/api/*
to the backend server running on port 8089.
My summer 2018 internship mentor Derek and I started this back in August 2018 as a side-project, but we only worked on it for a few weeks. In December 2019, while reading some articles about software design, I regained interest in this project and decided to apply some things that I've learned since then.
Some recent changes include adopting a domain driven structure/design, improving or redesigning various classes and components, adding a bunch of unit tests, adding JaCoCo code coverage reports, setting up Codacy and Codecov, and building the frontend with TypeScript 🚀
The backend is written in Java, utilizing Spring Boot for the REST API and websocket connections. The domain/business logic is decoupled from Spring — my goal with this was to learn how to write more testable code decoupled from framework code. The overall structure is inspired by my basic understanding of Domain Driven Design (DDD), but it's lacking some principles. Improvements will be made over time. Feel free to submit an issue or PR if you see something to be improved!
+----------------------+ +---------------------+ +----------------+
| | | | | |
| Presentation Layer +---->| Application Layer +---->| Domain Layer |
| | | | | |
+----------------------+ +---------------------+ +----------------+
^
|
+------------+--------+
| |
| Persistence Layer |
| |
+---------------------+
An arrow denotes a one-way dependency. Each layer may only reference objects from the layer(s) it depends on. This diagram was created with asciiflow.
-
Presentation Layer (web package): This is where all the web API and websocket controllers are. The "presentation" is in the form of JSON returned from the API, and STOMP messages sent to subscribed websocket clients via Spring Messaging. Controllers in the presentation layer interact with services in the application layer to complete their tasks.
-
Application Layer (application package): Application services live here. Application services primarily take in as input and return updated Data Transfer Objects (DTOs) - representations of the domain objects appropriate for 'public' consumption. Application services interact with the domain layer. Typically, the application services call some method from a repository to obtain an instance of a domain object hydrated with data.
-
Domain Layer (domain package): This is where the core game logic lives. It is standard Java code with no dependencies on other layers or on Spring. This layer contains domain entities modeled after the domain (Lost Cities), and the entities contain all the logic necessary for game play. The majority of this project's unit tests test the code here.
- Repository interfaces are also defined here
- Note that some entities contain JPA (Java Persistence API) annotations to enable the persistence layer to persist them. This isn't a dependency as they are only annotations (metadata).
-
Persistence Layer (persistence package): Implementations of repositories are defined here. The implemenations are where interactions with the database or other persistent data stores happen.
- Notice that the repository interfaces are defined in the domain layer. This allows layers to call repositories while being decoupled from the persistence layer. A repository implementation could use an in-memory data structure or a database to persist data; the layers using the repository wouldn't know or care.
- Considered part of the infrastructure layer in Domain Driven Design.
The frontend is written in TypeScript and uses Vue.js + Vuex. The Vuex stores are written using the vuex wrapper vuex-typex to enable typed methods with TypeScript. Some state is stored in the Vuex store, but the game state of the game being played/viewed is stored in the GamePlay
component as it is only accessed from that page.
Utilizes rx-stomp for STOMP over Websocket communication for sending game commands and receiving game state from backend in realtime. rx-stomp uses RxJS so observables can be subscribed to for STOMP queues/topics.