Disclaimer
I provide absolutely no guarantee, neither for the accuracy of this documentation nor for any property or feature of the software described here.
Do not use this software in critical situations or projects.
1. Introduction and Goals
Polly is a project for
-
letting me run live polls during a talk at a conference or meetup.
-
evaluating and learning certain technologies:
1.1. Requirements Overview
From a functional point of view, Polly is a web application for running a live poll during a talk or event. Consider it the digital version of the "show of hands", but with live display of what the audience voted.
From a project perspective, I use Polly to study and evaluate technology, patterns and frameworks. The functional requirements aren’t too complex, leaving me enough room for attention to quality goals.
-
Define a poll: a question with a predefined set of answers.
-
Define a poll instance, that is: one particular occurrence of a poll at a particular event.
-
Display an "event" view of a poll, consisting of a QR code for easy access to the voting page, and a graph that shows the live results.
-
This display should be embeddable in another HTML page.
-
-
Letting attendees vote on a poll instance.
I haven’t yet decided if I want or need a management user interface for defining those polls. For the first iteration, it may be enough to manually insert them into the persistent storage.
1.2. Main Quality Goals
The following list contains the most important quality goals for this application. The full list is in Quality Requirements.
ID | Quality | Motivation |
---|---|---|
QG01 |
Understandability |
Functional requirements should be simple and stay simple. This allows me to focus on the other goals (learning, experimenting) as well as writing quality code. |
QG02 |
Attractiveness |
Given I want to use Polly during events where I speak, the display should look good. |
QC03 |
Testability |
The architecture should allow easy testing of all main building blocks. |
1.3. Stakeholders
The following lists contains the most important personas for this application.
Role/Name | Goal/Boundaries |
---|---|
Developers |
Developers familiar with Java who want to learn about developing modern web applications without having to learn JavaScript/Typescript and an additional web application framework or two. |
Maarten Mulders (me) |
|
2. Architecture Constraints
2.1. Technical Constraints
Constraint | Background and / or motivation | |
---|---|---|
Software and programming constraints |
||
TC01 |
Use of Java |
One of the goals is to learn and evaluate certain technologies. I don’t want to learn too much new things at the same, so I stick to Java as the programming language. |
TC02 |
Use of open source technology |
Third-party (not written by me) software must be available under an open-source license. It should not be necessary to download or purchase proprietary software in order to study or contribute to this project. |
Operating System Constraints |
||
TC03 |
OS independent |
Developing and running the project should not require any particular operating system. It should be possible on at least Windows, Linux and macOS. |
2.2. Organisational Constraints
Constraint | Background and / or motivation | |
---|---|---|
OC1 |
Team |
Primarily myself, Maarten Mulders. Contributions from others are welcome, but I don’t expect too many. |
OC2 |
IDE independent setup |
Developing and running the project should not require any particular IDE or editor. The project must be compilable on the command line via standard build tools (e.g., Apache Maven). |
OC3 |
Configuration and version control / management |
Code is stored in a public Git repository with a complete commit history. This repository will host the code as well as this documentation. |
OC4 |
Testing |
Use JUnit to prove functional correctness using unit tests and optionally integration tests. Use JaCoCo to ensure a high test coverage and use PiTest to ensure high quality of tests. Use Playwright to write functional integration tests. Use Pitest and the Stryker Dashboard to run resp. display mutation testing (results). |
OC5 |
Published under an Open Source license |
The source code as well as the documentation should be published under an Open Source license. |
2.3. Conventions
Convention | Background and / or motivation | |
---|---|---|
C1 |
Architecture documentation |
Structure based on the (English) Arc42 template. |
C2 |
Coding conventions |
The project uses the Palantir Java format. Formatting is enforced through and can be executed using Spotless. |
C3 |
Natural language |
English. The project and the documentation both target an international audience. |
3. System Scope and Context
This chapter describes the environment and context of Polly. Who uses the system and on which other system(s) does Polly depend?
3.1. Business Context
Failed to generate image: Could not load PlantUML. Either require 'asciidoctor-diagram-plantuml' or specify the location of the PlantUML JAR(s) using the 'DIAGRAM_PLANTUML_CLASSPATH' environment variable. Alternatively a PlantUML binary can be provided (plantuml-native in $PATH). @startuml set separator none title Polly - System Context top to bottom direction skinparam { arrowFontSize 10 defaultTextAlignment center wrapWidth 200 maxMessageSize 100 } hide stereotype skinparam person<<Attendee>> { BackgroundColor #08427b FontColor #ffffff BorderColor #052e56 shadowing false } skinparam rectangle<<Polly>> { BackgroundColor #1168bd FontColor #ffffff BorderColor #0b4884 roundCorner 20 shadowing false } skinparam person<<Speaker>> { BackgroundColor #08427b FontColor #ffffff BorderColor #052e56 shadowing false } person "==Speaker\n<size:10>[Person]</size>\n\nSomeone presenting at a conference or meetup" <<Speaker>> as Speaker person "==Attendee\n<size:10>[Person]</size>\n\nSomeone attending a conference or meetup" <<Attendee>> as Attendee rectangle "==Polly\n<size:10>[Software System]</size>\n\nA web application for running a live poll during an event" <<Polly>> as Polly Speaker .[#707070,thickness=2].> Polly : "<color:#707070>Define, run and view poll" Attendee .[#707070,thickness=2].> Polly : "<color:#707070>Vote and view poll" @enduml
3.1.1. Speaker
A speaker at a conference or meetup would like to know how their audience feels about a subject. For that, they ask their audience a multiple-choice question, or poll.
Also, they would like to immediately see how their audience thinks about the question.
3.1.2. Attendee
Somebody attending a talk by Speaker who would love to participate in their poll(s).
Apart from providing their own view, they would also like to see how other Attendees feel about the same question.
3.2. Technical Context
The technical context of Polly is pretty simple. Polly runs as a web application and is accessible to users primarily using their web browser:
Interface | Description |
---|---|
HTTP |
Serves the views to interact with a poll; this includes both viewing and voting. |
4. Solution Strategy
Polly is a single deployable application using a relational database.
Failed to generate image: Could not load PlantUML. Either require 'asciidoctor-diagram-plantuml' or specify the location of the PlantUML JAR(s) using the 'DIAGRAM_PLANTUML_CLASSPATH' environment variable. Alternatively a PlantUML binary can be provided (plantuml-native in $PATH). @startuml set separator none title Polly - Containers left to right direction skinparam { arrowFontSize 10 defaultTextAlignment center wrapWidth 200 maxMessageSize 100 } hide stereotype skinparam rectangle<<Polly.Polly>> { BackgroundColor #438dd5 FontColor #ffffff BorderColor #2e6295 roundCorner 20 shadowing false } skinparam database<<Polly.PostgreSQL>> { BackgroundColor #438dd5 FontColor #ffffff BorderColor #2e6295 shadowing false } skinparam rectangle<<Polly>> { BorderColor #0b4884 FontColor #0b4884 shadowing false } rectangle "Polly\n<size:10>[Software System]</size>" <<Polly>> { database "==PostgreSQL\n<size:10>[Container]</size>\n\nPostgreSQL database" <<Polly.PostgreSQL>> as Polly.PostgreSQL rectangle "==Polly\n<size:10>[Container]</size>\n\nPolly application" <<Polly.Polly>> as Polly.Polly } Polly.Polly .[#707070,thickness=2].> Polly.PostgreSQL : "<color:#707070>Uses" @enduml
The architecture of Polly follows the ideas of hexagonal architecture. This means that its domain model is unaware of the technology that powers it. Each component lives in its own Maven module, which is also a Java module. This approach makes it easier to test a large part of the application logic in a fast and light-weight way [QC.OE.01].
The core of Polly is its Domain Model, which is completely agnostic of any framework or library. It applies a few tactical patterns from Domain Driven Design. You will find Repositories, rich Entities and Value Objects.
Since the Domain is technology-agnostic, some parts cannot be implemented in the Domain. That’s why the Infrastructure provides implementations based on the selected technology stack. For instance, it provides implementations of the Repositories based on the Jakarta Persistence API.
The Web application is responsible for generating the web-based user interface. It uses Krazo to deliver fast, light-weight web pages rather than requiring users to download a large bundle of JavaScript [QC.PF.01], [QC.PF.02].
To bring it all together without introducing undesired dependencies, the Application module packages all these components together. It creates a Web Archive (WAR) using overlays, that can be deployed to any Jakarta EE 10-compatible application server [QC.OE.01].
5. Building Block View
Polly is divided in three main parts: the (Core) Domain, a Web application that exposes functionality from the domain, and the Infrastructure that provides technology-dependent implementations for components that live in the domain.
Failed to generate image: Could not load PlantUML. Either require 'asciidoctor-diagram-plantuml' or specify the location of the PlantUML JAR(s) using the 'DIAGRAM_PLANTUML_CLASSPATH' environment variable. Alternatively a PlantUML binary can be provided (plantuml-native in $PATH). @startuml set separator none title Polly - Polly - Components left to right direction skinparam { arrowFontSize 10 defaultTextAlignment center wrapWidth 200 maxMessageSize 100 } hide stereotype skinparam rectangle<<Polly.Polly.Domain>> { BackgroundColor #85bbf0 FontColor #000000 BorderColor #5d82a8 roundCorner 20 shadowing false } skinparam rectangle<<Polly.Polly.Infrastructure>> { BackgroundColor #85bbf0 FontColor #000000 BorderColor #5d82a8 roundCorner 20 shadowing false } skinparam rectangle<<Polly.Polly.Webapplication>> { BackgroundColor #85bbf0 FontColor #000000 BorderColor #5d82a8 roundCorner 20 shadowing false } skinparam rectangle<<Polly.Polly>> { BorderColor #2e6295 FontColor #2e6295 shadowing false } rectangle "Polly\n<size:10>[Container]</size>" <<Polly.Polly>> { rectangle "==Domain\n<size:10>[Component]</size>" <<Polly.Polly.Domain>> as Polly.Polly.Domain rectangle "==Infrastructure\n<size:10>[Component]</size>" <<Polly.Polly.Infrastructure>> as Polly.Polly.Infrastructure rectangle "==Web application\n<size:10>[Component]</size>" <<Polly.Polly.Webapplication>> as Polly.Polly.Webapplication } Polly.Polly.Infrastructure .[#707070,thickness=2].> Polly.Polly.Domain : "<color:#707070>Enables" Polly.Polly.Infrastructure .[#707070,thickness=2].> Polly.Polly.Webapplication : "<color:#707070>Enables" Polly.Polly.Webapplication .[#707070,thickness=2].> Polly.Polly.Domain : "<color:#707070>Interacts with" @enduml
As outlined in the Solution Strategy, the domain model must be unaware of the technology that powers it. It is the responsibility of the Infrastructure module to provide the actual implementations for concerns like persistent storage of domain entities.
6. Runtime View
Polly leverages the Jakarta MVC specification and its reference implementation Krazo. The typical implementation for a page that displays information is depicted below.
Failed to generate image: Could not load PlantUML. Either require 'asciidoctor-diagram-plantuml' or specify the location of the PlantUML JAR(s) using the 'DIAGRAM_PLANTUML_CLASSPATH' environment variable. Alternatively a PlantUML binary can be provided (plantuml-native in $PATH). @startuml hide footbox actor user as "User/Browser" control controller as "Controller" entity model as "Model" participant view as "View" user -> controller : Request page activate controller controller -> model : Retrieve activate model controller <- model deactivate model create view controller -> view : Select controller -> view : Render deactivate controller activate view view -> model : Read activate model view <- model deactivate model view -> user : Rendered page destroy view @enduml
The typical implementation for a page that modifies information is slightly more complicated.
Failed to generate image: Could not load PlantUML. Either require 'asciidoctor-diagram-plantuml' or specify the location of the PlantUML JAR(s) using the 'DIAGRAM_PLANTUML_CLASSPATH' environment variable. Alternatively a PlantUML binary can be provided (plantuml-native in $PATH). @startuml hide footbox actor user as "User/Browser" control controller as "Controller" entity model as "Model" participant view as "View" user -> controller : Request page activate controller controller -> controller : Validate input controller -> model : Retrieve activate model controller <- model deactivate model controller -> controller : Verify input opt Input validation/verification failed controller -> model : Add error messages activate model controller <- model deactivate model create view controller -> view : Select controller -> view : Render deactivate controller activate view view -> model : Read activate model view <- model deactivate model view -> user : Rendered page destroy view end controller -> model : Modify activate model controller <- model deactivate model create view controller -> view : Select controller -> view : Render deactivate controller activate view view -> model : Read activate model view <- model deactivate model view -> user : Rendered page destroy view @enduml
7. Deployment View
7.1. Network infrastructure
The solution is deployed on virtual machines in the Oracle Cloud. The deployment is spread across three virtual machines, each with their own role.
-
The machine labelled "proxy" is a virtual machine running nginx. nginx is configured to act as a reverse proxy for an upstream HTTP server.
-
The machine labelled "apps" is a virtual machine running the OpenLiberty application server.
-
The machine labelled "database" is a virtual machine running the PostgreSQL database server.
These machines are connected through a Virtual Cloud Network. Only the machine labelled "proxy" is exposed to the internet.
All machines can be deployed in the free tier, which allows (among other things) for 2 AMD-based virtual machines up to 4 Arm-based virtual machines, and 2 Virtual Cloud Networks. |
The deployment design is depicted below.
Failed to generate image: Could not load PlantUML. Either require 'asciidoctor-diagram-plantuml' or specify the location of the PlantUML JAR(s) using the 'DIAGRAM_PLANTUML_CLASSPATH' environment variable. Alternatively a PlantUML binary can be provided (plantuml-native in $PATH). @startuml 'Define the main location to the OCI icons for PlantUML !define OCIPuml https://raw.githubusercontent.com/rUser75/OCI-icons-for-plantuml/v1.2/dist 'Include main OCICommon and then resource files !include OCIPuml/OCICommon.puml !include OCIPuml/Groups/all.puml !include OCIPuml/Compute/all.puml oci_RegionGroup(eu-frankfurt-1, eu-frankfurt-1) { oci_VCNGroup(apps) { oci_VirtualMachine(proxy, Proxy, AMD-based VM) { } oci_VirtualMachine(apps, Applications, Arm-based VM) { } oci_VirtualMachine(database, Database, Arm-based VM) { } } } @enduml
The application components are distributed over the infrastructure according to the following diagram
Failed to generate image: Could not load PlantUML. Either require 'asciidoctor-diagram-plantuml' or specify the location of the PlantUML JAR(s) using the 'DIAGRAM_PLANTUML_CLASSPATH' environment variable. Alternatively a PlantUML binary can be provided (plantuml-native in $PATH). @startuml actor User cloud "Oracle Cloud" { node proxy { stack nginx { component "Proxy server" } } node apps { stack OpenLiberty { component "Polly Application" } } node database { database PostgreSQL { artifact "database\nschema" } } } User -right-> "Proxy server" "Proxy server" -right-> "Polly Application" "Polly Application" -right-> "database\nschema" @enduml
10. Quality Requirements
10.1. Quality Tree
The following list contains the quality goals for this application. The goals with an identifier between square brackets correspond with one of the Main Quality Goals.
ID | Quality Category | Quality | Description | Scenario |
---|---|---|---|---|
QC.US.01 |
Usability |
Attractiveness [QG02] |
I want to use Polly during events where I speak, so displaying polls should look good. |
|
QC.PF.01 |
Performance |
Responsiveness |
Displaying a poll during a talk should take less than 500 milliseconds. |
|
QC.PF.02 |
Responsiveness |
Voting on a poll should take less than 1 second. |
||
QC.OE.01 |
Operational & Environmental |
Runtime agnosticism |
The solution should work on any Jakarta EE 10 compliant application server. |
|
QC.MS.01 |
Maintainability & Support |
Understandability [QC01] |
Functional requirements should be simple and stay simple. This allows me to focus on the other goals (learning, experimenting) as well as writing quality code. |
|
QC.TS.01 |
Testability [QC03] |
The architecture should allow easy testing of all main building blocks. |