Skip to content

Commit 50aa3c7

Browse files
Development guidelines (#83)
1 parent 89c345a commit 50aa3c7

File tree

2 files changed

+90
-0
lines changed

2 files changed

+90
-0
lines changed

CONTRIBUTING.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ Whitefox follows a standard
1010
[fork and pull](https://help.github.com/articles/using-pull-requests/)
1111
model for contributions via GitHub pull requests.
1212

13+
Software development guidelines that we employ can be found [here](docs/development_guidelines.md).
14+
1315
Below is a list of the steps that might be involved in an ideal
1416
contribution. If you don't have the time to go through every step,
1517
contribute what you can, and someone else will probably be happy to

docs/development_guidelines.md

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# Development guidelines
2+
3+
# Getting started
4+
5+
To get started you need:
6+
7+
- JDK 11 installed
8+
9+
alternatively a recent IntelliJ IDEA Community Edition version is enough.
10+
11+
The project is built using [Gradle](https://gradle.org), the build is defined using Gradle Kotlin DSL. If you never used
12+
Gradle I kindly suggest to start from [its awesome docs](https://gradle.org/guides/#getting-started).
13+
14+
## Server
15+
16+
The server is [quarkus](https://quarkus.io) application that exposes a REST API implementing Whitefox and
17+
delta-sharing protocol. If you never worked with Quarkus, have a look at [its awesome doc](https://quarkus.io/get-started/).
18+
19+
The app is developed and run for JVM 11 because hadoop libraries (which are a dependency of delta-lake kernel) do
20+
not run properly on newer versions of the JVM.
21+
22+
As soon as you clone the project you should verify that you are able to build and test locally, to do so you need to
23+
run the `check` command of Gradle, you can achieve that using either `gradlew` script in the project root (`.
24+
/gradlew check`) or run the same [gradle task from intellij](https://www.jetbrains.com/help/idea/work-with-gradle-tasks.
25+
html). If you're default jvm is not version 11, you can run `gradlew` passing another java home as follows:
26+
`./gradlew -Dorg.gradle.java.home=<PATH_TO_JAVA_HOME> build`.
27+
28+
Sometimes IntelliJ will tell you have build errors, especially when moving from one branch to the other. The problem
29+
might be that the auto-generated code (we generate stubs from openapi) is out of date. A simple `compileJava` will
30+
fix it.
31+
32+
Before every push you ~~can~~ should run locally `./gradlew devCheck` that will both reformat the code and run the
33+
unit tests.
34+
35+
You can run the server locally with the command: `gradlew server:quarkusDev`
36+
37+
### Windows
38+
39+
Some hadoop related tests do not run on Windows (especially in the Windows CI) so you can find the disabled using junit5
40+
annotation `@DisabledOnOs(WINDOWS)`. If you want to provide a fix for them, feel free to open a PR.
41+
42+
## Code formatting
43+
44+
We use spotless to format and check the style of Java code. Spotless can be run through Gradle:
45+
46+
- to reformat: `./gradlew spotlessApply`
47+
- to check formatting: `./gradlew spotlessCheck`
48+
49+
## Protocol
50+
51+
Whitefox protocol and original delta-sharing are kept under [`docs/protocol`](protocol). We use openapi
52+
specification to generate code for server and client. Furthermore, the protocol itself is validated using [spectral]
53+
(https://stoplight.io/open-source/spectral), you can find spectral configuration at [`.spectral.yaml`](../.spectral.yaml).
54+
55+
56+
# Software Engineering guidelines
57+
58+
Some golden rules that we use in this repo:
59+
60+
- always prefer immutability, this helps writing correct concurrent code
61+
- use constructor-based dependency injection when possible (i.e. do not annotate with `@Inject` any field) this
62+
makes testing beans easy without a CDI framework (that makes unit test much faster)
63+
- the "core" code should [make illegal states unrepresentable](https://khalilstemmler.com/articles/typescript-domain-driven-design/make-illegal-states-unrepresentable/)
64+
- never use `null`, use `Optional` to convey optionality, ignore IntelliJ "warning" when passing `Optional` to methods
65+
- throw exceptions and write beautiful error messages
66+
- use `@QuarkusTest` annotation only when you really need the server to be running, otherwise write a "simple" unit
67+
test
68+
- don't use `@SessionScoped` it will make our life miserable when trying to scale horizontally
69+
- [use `@ApplicationScoped` everywhere, `@Singleton` when `@ApplicationScoped` does not fit](https://quarkus.io/guides/cdi#applicationscoped-and-singleton-look-very-similar-which-one-should-i-choose-for-my-quarkus-application)
70+
- de-couple different layers of the application as much as possible:
71+
- REST API classes should deal with auto-generated model classes, convert them (when needed) to the "core"
72+
representation and pass it to "services"
73+
- services should not know anything about the outside world (i.e. REST API) and should deal with "core" models
74+
that do not depend on any given serialization format
75+
- persistence is its own world too, if needed we can create "persistence-related" classes to use ORM tools but
76+
these should not interfere in any way with the core ones
77+
- we will hand-craft (for now, who knows in the future) a lot of `mappers` that are function that go from one
78+
"layer" to another (REST API/persistence/core are all layers)
79+
80+
For example, you can see the `DeltaSharesApiImpl` class, it deals with jakarta annotations to define http endpoints,
81+
it deals with errors thrown by the underlying (core) service and transforms those errors into 500/400 status codes
82+
and related http response and performs "protocol adaptation" which is transform http requests into core objects,
83+
then invokes the underlying core service and transforms the output of the service into a http response. `DeltaSharesApiImpl`
84+
has no business logic, it performs only "mappings" between the internal core model (made of methods, exceptions
85+
classes) to the external world made of json payloads and http response/requests.
86+
87+
On the other hand `DeltaSharesServiceImpl` is business logic, it knows nothing about http, therefore it does not
88+
depend on any auto-generated code from the openapi spec.

0 commit comments

Comments
 (0)