forked from rubenDTD/urlshortener
-
Notifications
You must be signed in to change notification settings - Fork 0
/
README.qmd
179 lines (134 loc) · 7.57 KB
/
README.qmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
---
title: Getting Started with the URL Shortener project
date: last-modified
format: gfm
---
::: {.content-visible when-format="gfm"}
[![](https://github.com/UNIZAR-30246-WebEngineering/urlshortener/actions/workflows/ci.yml/badge.svg)](https://github.com/UNIZAR-30246-WebEngineering/urlshortener/actions/workflows/ci.yml/badge.svg)
:::
## System requirements
This application leverages cutting-edge technologies to deliver a robust and versatile user experience:
1. **Programming Language**: The application is written in [Kotlin 2.0.20](https://kotlinlang.org/), a versatile, open-source, statically-typed language. Kotlin is renowned for its adaptability and is commonly used for Android mobile app development. Beyond that, it finds application in server-side development, making it a versatile choice.
2. **Build System**: The application utilizes [Gradle 8.5](https://gradle.org/) as its build system. Gradle is renowned for its flexibility in automating the software building process. This build automation tool streamlines tasks such as compiling, linking, and packaging code, ensuring consistency and reliability throughout development.
3. **Framework**: The application employs [Spring Boot 3.3.3](https://docs.spring.io/spring-boot/) as a framework. This technology requires Java 17 and is fully compatible up to and including Java 21. Spring Boot simplifies the creation of production-grade [Spring-based applications](https://spring.io/). It adopts a highly opinionated approach to the Spring platform and third-party libraries, enabling developers to initiate projects with minimal hassle.
## Overall structure
The structure of this project is heavily influenced by [the clean architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html):
* A `core` module where we define the domain entities and the functionalities (also known as use cases, business rules, etc.). They do not know that this application has a web interface or that data is stored in relational databases.
* A `repositories` module that knows how to store domain entities in a relational database.
* A `delivery` module that knows how to expose the functionalities on the web.
* An `app` module that contains the main application, the configuration (i.e., it links `core`, `delivery`, and `repositories`), and the static assets (i.e., HTML files, JavaScript files, etc.).
```{mermaid}
%%| fig-width: 6
%%| fig-align: center
%%| fig-caption: The overall structure of the project
flowchart LR;
User-- HTTP -->Tomcat("Embedded<br>Web Server<br><b>Apache Tomcat")
subgraph "Application <b>UrlShortener</b>"
Tomcat== "Dynamic<br>resources" ==>Delivery("Module<br><b>delivery</b>")
Tomcat== "Static<br>resources" ==>App("Module<br><b>app</b>")
Tomcat~~~App("Module<br><b>app</b>")
App-. configure .->Tomcat
App-. configure .->Delivery
App-. configure .->Core
App-. configure .->Repositories
subgraph Core [Module<br><b>core</b>]
PortA("Port")==>UseCases("Use<br>Cases")
UseCases==>PortB("Port")
end
PortB==>Repositories("Module<br><b>repositories</b>")
Delivery==>PortA
end
Repositories-- JDBC -->Database[(Database)]
```
Usually, if you plan to add a new feature:
* You will add a new use case to the `core` module.
* If required, you will modify the persistence model in the `repositories` module.
* You will implement a web-oriented solution to expose it to clients in the `delivery` module.
Sometimes, your feature will not be as simple, and it may require:
* Connecting to a third party (e.g., an external server). In this case, you will add a new module named `gateway` responsible for such a task.
* An additional application. In this case, you can create a new application module (e.g., `app2`) with the appropriate configuration to run this second server.
Features that require connecting to a third party or having more than a single app will be rewarded.
## Run
The application can be run as follows:
```bash
./gradlew bootRun
```
Now you have a shortener service running at port 8080. You can test that it works as follows:
```bash
$ curl -v -d "url=http://www.unizar.es/" http://localhost:8080/api/link
* Trying ::1:8080...
* Connected to localhost (::1) port 8080 (#0)
> POST /api/link HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.71.1
> Accept: */*
> Content-Length: 25
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 25 out of 25 bytes
* Mark bundle as not supporting multiuse
< HTTP/1.1 201
< Location: http://localhost:8080/tiny-6bb9db44
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Tue, 28 Sep 2021 17:06:01 GMT
<
* Connection #0 to host localhost left intact
{"url":"http://localhost:8080/tiny-6bb9db44","properties":{"safe":true}}%
```
And now, we can navigate to the shortened URL.
```bash
$ curl -v http://localhost:8080/6bb9db44
* Trying ::1:8080...
* Connected to localhost (::1) port 8080 (#0)
> GET /tiny-6bb9db44 HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.71.1
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 307
< Location: http://www.unizar.es/
< Content-Length: 0
< Date: Tue, 28 Sep 2021 17:07:34 GMT
<
* Connection #0 to host localhost left intact
```
## Build and Run
The uberjar can be built and then run with:
```bash
./gradlew build
java -jar app/build/libs/app-0.2024.1-SNAPSHOT.jar
```
## Functionalities
The project offers a minimum set of functionalities:
* **Create a short URL**. See in `core` the use case `CreateShortUrlUseCase` and in `delivery` the REST controller `UrlShortenerController`.
* **Redirect to a URL**. See in `core` the use case `RedirectUseCase` and in `delivery` the REST controller `UrlShortenerController`.
* **Log redirects**. See in `core` the use case `LogClickUseCase` and in `delivery` the REST controller `UrlShortenerController`.
The objects in the domain are:
* `ShortUrl`: the minimum information about a short URL
* `Redirection`: the remote URI and the redirection mode
* `ShortUrlProperties`: a handy way to extend data about a short URL
* `Click`: the minimum data captured when a redirection is logged
* `ClickProperties`: a handy way to extend data about a click
## Delivery
The above functionality is available through a simple API:
* `POST /api/link` which creates a short URL from data send by a form.
* `GET /{id}` where `{id}` identifies the short URL, deals with redirects, and logs use (i.e. clicks).
In addition, `GET /` returns the landing page of the system.
## Repositories
All the data is stored in a relational database. There are only two tables.
* **shorturl** that represents short URLs and encodes in each row `ShortUrl` related data,
* **click** that represents clicks and encodes in each row `Click` related data.
## Reference Documentation
For further reference, please consider the following sections:
* [Official Gradle documentation](https://docs.gradle.org)
* [Spring Boot Gradle Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/current/gradle-plugin/reference/htmlsingle/)
* [Spring Web](https://docs.spring.io/spring-boot/reference/web/index.html)
* [Spring SQL Databases](https://docs.spring.io/spring-boot/reference/data/sql.html)
## Guides
The following guides illustrate how to use some features concretely:
* [Building a RESTful Web Service](https://spring.io/guides/gs/rest-service/)
* [Serving Web Content with Spring MVC](https://spring.io/guides/gs/serving-web-content/)
* [Building REST services with Spring](https://spring.io/guides/tutorials/rest/)
* [Accessing Data with JPA](https://spring.io/guides/gs/accessing-data-jpa/)