Skip to content
A Spring Boot and AngularJS boilerplate project.
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
Checking_System Switched review system from JMS based listeners to database based app… Dec 28, 2017
Commons_Library Add interface for domain mapper and updated readme accordingly Dec 29, 2017
E2E_Tests Add example E2E tests simulating an user Nov 7, 2017
Review_System Add interface for domain mapper and updated readme accordingly Dec 29, 2017
Statistics_System Switched review system from JMS based listeners to database based app… Dec 28, 2017
User_Interface_System Change all project names to upper case Nov 9, 2017
gradle/wrapper Updated Gradle Wrapper version to latest 4.3 Nov 2, 2017
.gitignore
.travis.yml Add basic .travis.yml Nov 9, 2017
Readme.md Add instructions to start projects individually Feb 2, 2018
build.gradle
docker-compose.yml
gradlew Updated Gradle Wrapper version to latest 4.3 Nov 2, 2017
gradlew.bat Updated Gradle Wrapper version to latest 4.3 Nov 2, 2017
settings.gradle

Readme.md

Review System Demo

A Spring-Boot and AngularJS boilerplate project

Build Status

About

This is a boilerplate project to show a message driven architecture for Spring Boot projects. Note that this application is highly opinionated and might not fit to your needs or preferences.

Read more: http://tuhrig.de/spring-boot-boilerplate-project-with-activemq-and-angularjs

Tags

Spring Boot, AngularJS, Domain Driven Design, Lombok, ActiveMQ

Use cases of the demo

  • An user can submit a review about a product.
  • Each review will be checked and approved (or rejected).
  • All approved reviews can be listed.
  • Statistics about how many reviews have been submitted, approved and rejected can be seen.

Systems

User Interface System (port 9002)

  • AngularJS based user interface
  • Allows to submit and read reviews and view some overall statistics
  • The backend is a Zuul proxy which forwards requests (to the review system)

Review System (port 9001)

  • Takes and persists all reviews
  • Throws NEW REVIEW events to inform other system about new reviews
  • Listens for APPROVED REVIEW events and REJECTED REVIEW events

Checking System (port 9000)

  • Checks incoming reviews to either approve or reject them
  • Listens for NEW REVIEW events
  • Throws APPROVED REVIEW events or REJECTED REVIEW events

Statistics System (port 9003)

  • Listens for NEW REVIEW, APPROVED REVIEW and REJECTED REVIEW event
  • Provides simple statistics on how many reviews have been submitted, approved or rejected

Run

Perquisites:

  • Java must be installed
  • docker and docker-compose must be installed

Run it:

docker-compose up
./gradlew bootrun --parallel --max-workers=4

Or:

./gradlew review_system:bootrun
./gradlew statistics_system:bootrun
./gradlew checking_system:bootrun
./gradlew user_interface_system:bootrun   

Go to: http://localhost:9002

For development:

Test

Java Tests

./gradlew check

JavaScript Tests

cd User_Interface_System/
grunt check

(Protractor) E2E-Tests

cd E2E_Tests/
grunt protractor

Note: In order to run the E2E tests, all four systems (review, statistics, checking and UI) must be running. The E2E tests will then open a Chrome browser to navigate on the web application.

Developer Notes

Packages

The package structure follows the Ports-And-Adapters pattern.

Domain IDs

Domain IDs identify entities. They are unique business keys and should have a meaningful structure (not just database increments).

Annotations:

  • @Value as its immutable

Example: ReviewId.java

Domain Entities

A domain entity contains actual business functionality. Every domain entity is identified by its ID (not by its current data or state).

Annotations:

  • @Builder to be able to build the entity when loading it from the database
  • @AllArgsConstructor(access = AccessLevel.PRIVATE) to enable the builder
  • @Getter to map the entity to REST resources
  • @Slf4j to log when executing business functionality
  • @EqualsAndHashCode(of = "myEntityId") to enforce equality based on the entity ID

Rules:

  • The only place to implement business logic
  • Should be free of any framework dependency
  • No JPA or Jackson annotations

Example: Review.java

Domain Events

A message send to the world which informs about some event that has happened in the past.

Annotations:

  • @Data as the events are simple containers to exchange information

Rules:

  • No logic, just simple POJOs to hold data
  • Try to use only simple types (e.g. String, int, boolean) to guarantee easy serialisation

Example: ReviewSubmittedEvent.java

REST Resources

Annotations:

  • @Data for simple containers to exchange information

Rules:

  • No logic
  • Use simple types (e.g. String, int, boolean) to guarantee easy serialisation
  • Only use in REST layer

Example: ReviewResource.java

JPA Entities

Annotations:

  • @Data for simple data containers
  • @Entity(name = "MY_TABLE_NAME") to enable JPA on this entity
  • @EntityListeners(AuditingEntityListener.class) to enable auditing (created and last modified date)

Rules:

  • No logic
  • Use simple types (e.g. String, int, boolean) to have no dependencies outside
  • Only use in database layer
  • An entity mapper class to convert to and from the domain layer

Example: ReviewEntity.java

Event Listeners

I've implemented two different types of event listeners. Both solutions have their own pros and cons.

Type 1: JMS based listeners

@Component
public class ReviewSubmittedEventListener {

    @JmsListener(
            destination = "Consumer.statistics_system.VirtualTopic.Events",
            selector = "_type = 'ReviewSubmittedEvent'"
    )
    public void onEvent(ReviewSubmittedEvent event) {
        // pass the event to the service layer
    }
}

Rules:

  • No logic. Just take the event and pass it to a service
  • Use of Spring's @JmsListener annotation

Example: ReviewSubmittedEventListener.java

Type 2: Database based listeners

@Component
public class ReviewApprovedEventListener extends MessageHandler<ReviewApprovedEvent> {

    @Override
    public void onMessage(ReviewApprovedEvent reviewApprovedEvent) {
        // pass the event to the service layer
    }
}

Rules:

  • No logic. Just take the event and pass it to a service
  • Extend the custom MessageHandler class which polls a database table for events

Example: ReviewApprovedEventListener.java

Domain Object Mapper

Whenever we map between the domain layer and any other layer (e.g. persistence, REST) we use a dedicated mapper to do so. Although it's not needed, every mapper should implement the DomainObjectMapper.java interface. By implementing this interface, the concept of a dedicated mapper class is underlined and every mapper follows the same structure.

Rules:

  • Don't map in-place - write a mapper object
  • Implement the DomainObjectMapper.java interface to underline the concept
  • Accept null and simply return null

Example: MessageEntityMapper.java

You can’t perform that action at this time.