- Install JDK 11 and Docker
cd tools
./start-local-env.sh
to bring up all needed services- Run the application via the
Run (dev)
IntelliJ Run Configuration or with./mvnw spring-boot:run -Dspring.profiles.active=dev
In a real project you probably wouldn't generate fake data for each developer, you'd probably have a separate repo with seed data for local dev envs.
- Run the
Generate Fake Data
IntelliJ Run Configuration or./mvnw springboot:run generateFakeData --numBooks=10000 --outputFile=fakeData.10k.sql
- Run the
Seed DB
IntelliJ Run Configuration or./mvnw springboot:run seedDb --inputFile=fakeData.10k.sql
- Run the
Seed Search
IntelliJ Run Configuration or./mvnw springboot:run seedSearch
This app is a classic 3-tier architecture:
We've found (over time) these annotations cause some headaches, so we rarely use these anymore.
- cascading updates
- accidentally modify a child
- N+1 queries
- oh shoot, we rarely need that field, better add
FetchType.LAZY
!
- oh shoot, we rarely need that field, better add
- naive toString() problems
- A contains B and B contains A, toString() causes a
StackOverflowException
- A contains B and B contains A, toString() causes a
Represents API (REST) models. These are what Controllers receive as input and produce as output. These can be generated with Immutables since it has Jackson support.
Represents DTOs. These should be used in the Service layer of the application. Feel free to add these to interfaces to cross-package communication. Usually generated via Immutables unless there's a good reason not to.
Represents Mappers (classes that convert from one type to another).
Represents Exception
s that can be thrown by this package.
It's usually a good idea to extend ErrorCodeException
and provide package-specific ErrorCode
s.
Represents DB models. These are POJO's (Plain Old Java Objects) with some annotations (@Column
, @Entity
) to assist with JPA mapping these objects.
Not generated by Immutables since JPA objects have some pitfalls when generated or proxied.
Usually it's sufficient to just check the @Id
of the class (typically a UUID
) in these methods.
Try not to expand relations in toString (ie: A.toString()
prints B
and/or B.toString()
prints A
).
This makes debugging slow (and in this case would throw a StackOverflowException
).
Printing the ID of the relation is much faster if you need this data in the String.
(omitting the name.marcocirillo.library prefix)
Represents an Account. We didn't call them User
s since user
is a reserved word in Postgres and having to keep tracks of escapes and casing is a nightmare.
Handles authentication. Auth providers must implement AuthService to provide auth to the platform. SomeAuth
is the dummy auth provider we used in this example.
Represents an Author of a Book.
Base classes used by other packages go here.
Represents a Book.
Represents a Category assigned to a Book. Multiple Categories can be assigned to a Book.
Powers Favourite Books, an email Notification that sends all members their favourite books.
Represents Categories
Handles checking out Books. package checkout.validator
contains validators for Checkouts, to ease in testing and to keep CheckoutService
smaller.
Powers Overdue Books, an email Notification sent to Accounts when books are overdue.
Powers CLI commands for the app (ie: ./mvnw springboot:run seedDb
would have a corresponding SeedDbCommand
class).
Global app configuration. If a config isn't global (CronConfig
is an example), it can be localized to its own package (CronConfig
is only for cron jobs, so it's in the cron
package).
Powers cron jobs for the app.
Generates dummy data for the app. In a real project, this would likely be in its own repository.
Spring Filter
s (middlewares) that run before hitting Controller
s.
Represents Inventory (number of Books in stock that can be checked out).
Handles sending notifications.
Handles search.
Handles seeding (filling in data to) the DB and search indices.
Small utilities used by other packages.
Handles Spring web configuration, including security (in the web.security
package).