Skip to content

Commit 44d1f7b

Browse files
committed
Finalise Django ADR
1 parent f8c78d1 commit 44d1f7b

File tree

1 file changed

+45
-21
lines changed

1 file changed

+45
-21
lines changed

docs/adr/ADR-XXX-Use_Django_framework.md renamed to docs/adr/ADR-001-Use_Django_framework.md

+45-21
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
>| | |
44
>| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
5-
>| Date | `01/04/2025` _when the decision was last updated_ |
5+
>| Date | `17/04/2025` _when the decision was last updated_ |
66
>| Status | `Proposed` |
77
>| Deciders | `Team` |
88
>| Significance | `Structure` |
@@ -30,7 +30,7 @@ We're deciding initial technologies for the service. We're working under the ass
3030
[Reasons for adopting a framework are outlined in the quality framework.](https://github.com/NHSDigital/software-engineering-quality-framework/blob/6d5453d3ee0f9c7af1a4b48d2ec8cb72d9826460/practices/structured-code.md?plain=1#L24) Our app is not trivial enough to build without a framework, and we are assuming it will be fairly typical for a web application running
3131
in the cloud: a stateless service that renders HTML from tempalates, an ORM that connects to a Postgres database, etc.
3232

33-
We're initially intending to productionise the [manage screening events prototype](https://github.com/NHSDigital/manage-screening-events-prototype). We're anticipating building an additional gateway service that would be deployed inside trusts' networks, which this app would communicate with via a queue or API (to be decided later).
33+
We're initially intending to productionise the [manage screening events prototype](https://github.com/NHSDigital/manage-screening-events-prototype). We're anticipating that we may build an additional gateway service that would be deployed inside trusts' networks, which this app would communicate with via a queue or API, but this is to be decided later.
3434

3535

3636
## Decision
@@ -39,16 +39,20 @@ We're initially intending to productionise the [manage screening events prototyp
3939

4040
### Drivers
4141

42+
- We're building a web application with an accessible frontend, not a microservice
4243
- Convention over configuration: the framework should provide reasonable, secure defaults, and be familiar enough for new team members joining the team
4344
- Easy to get started with
4445
- Easy to extend while maintaining separation of concerns
4546
- Limited boilerplate
4647

4748
### Options
4849

49-
We are considering the two most common frameworks in Python: Django and Flask. Both are stable, established frameworks, with large open source communities. Flask is on the Technology Radar, and is being used for several APIs. Django is not on the radar but is used by the [Transformation directorate website](https://github.com/nhsx/nhsx-website).
50+
We considered the two most common frameworks in Python: Django and Flask. Both are stable, established frameworks, with large open source communities. Flask is on the Technology Radar, and is being used for several APIs. Django is not on the radar but is used by the [Transformation directorate website](https://github.com/nhsx/nhsx-website).
51+
52+
In addition to this repo, we created a [technical spike for Flask](https://github.com/NHSDigital/manage-breast-screening-flask-spike).
5053

5154
#### ORM
55+
5256
Django has a built in ORM and migrations framework. The Django ORM uses the simpler [ActiveRecord pattern](https://www.thoughtfulcode.com/orm-active-record-vs-data-mapper/), whereas a Flask app would use SQLAlchemy, which implements the Data mapper pattern.
5357

5458
Django's ORM is well documented and easy to get started with:
@@ -63,24 +67,34 @@ In general, Django encourages a tightly coupled model layer, but this comes with
6367
the view / form / model abstractions work well together. It is possible to abstract away the ORM behind a service layer, but then you lose a lot of these helpful abstractions (e.g. model forms, pagination helpers etc).
6468

6569
#### Templating
70+
6671
Django has its own templating language, but it also supports Jinja.
6772
With a few minor adjustments, we can reuse the NHS.UK frontend templates.
6873

6974
Flask uses Jinja by default.
7075

76+
#### Routing
77+
78+
Flask has a fairly robust routing system, using "blueprints" to organise and group routes in different parts of the app. Django has a comparable system which is also flexible and easy to keep organised.
79+
7180
#### View logic
81+
7282
Django includes [Generic class-based views](https://docs.djangoproject.com/en/5.1/topics/class-based-views/generic-display/), which can be used to reduce boilerplate in the view layer. The trade-off is they are a bit more unintuitive to work with compared to function based views. We have the option of using either or both.
7383

7484
Django has a [form abstraction](https://docs.djangoproject.com/en/5.2/topics/forms/) that simplifies the rendering and processing of form pages. The form layer is completely decoupled from the model layer, but you would typically use model forms as described above.
7585

7686
If we decide to build APIs, we can use [Django Rest Framework](https://www.django-rest-framework.org/) - a framework built on top of Django that handles serialisation/deserialisation, API authentication, and OpenAPI specifications.
7787

7888
#### Authentication system
89+
7990
Django comes bundled with an [authentication system](https://docs.djangoproject.com/en/5.1/ref/contrib/auth/) which handles accounts, permissions, and user sessions.
8091

8192
We would be using this in combination with an OIDC library such as [Django-AllAuth](https://docs.allauth.org/en/latest/) or [Authlib](https://docs.authlib.org/en/latest/client/django.html).
8293

94+
Flask would need auth built out manually using a library like flask-OAuth.
95+
8396
#### Learning curve
97+
8498
Django is a larger framework, so there is more to learn, but less moving parts.
8599

86100
Since the framework has more conventions for doing things than flask, it is easier to move between Django
@@ -89,48 +103,58 @@ projects, and if you get stuck it's a bit easier to find people who've solved th
89103
We have some Django knowledge on the team already, and Django is conceptually very similar to other major web frameworks such as Rails, which the team is already familiar with.
90104

91105
#### Code structure
106+
107+
Flask is completely flexible and unopinionated, meaning that every decision about where to put things will be up to this team. This could lead to a project structure that has more of a learning overhead for new developers.
108+
109+
Django has a more prescriptive structure, which potentially means a project structure that is more familiar to new developers.
110+
92111
Django encourages you to factor your Django project into modular [apps](https://docs.djangoproject.com/en/5.2/ref/applications/), rather than a single monolithic package. An app may correspond to a [bounded context in domain-driven design](https://martinfowler.com/bliki/BoundedContext.html) or a cross-cutting capability.
93112

94113
[It is possible to write apps in such a way that can be reused and configured in other projects.](https://docs.djangoproject.com/en/5.2/intro/reusable-apps/).
95114

96115
There is a large ecosystem of 3rd party apps available.
97116

98117
#### Security
118+
99119
Django has built in protections for common web app vulnerabilities, such as XSS, SQL injections, CSRF.
100120

101121
There is guidance and checklists in the [Django security guide](https://docs.djangoproject.com/en/5.2/topics/security/) and [How to deploy Django](https://docs.djangoproject.com/en/5.2/howto/deployment/).
102122

123+
Flask has basic session management and XSS protection via its use of jinja2 templates. Other protections would need to come from external libraries, which we'd need to select manually based on a list of common vulnerabilities.
124+
103125
#### Testing
126+
104127
Django comes with very good [testing tools](https://docs.djangoproject.com/en/5.2/topics/testing/tools/). Out of the box you can test database interactions (rolling back to a fresh state between tests), and emulated HTTP requests.
105128

106129
By default Django uses unittest as the test framework, but you can easily swap this out for [pytest](https://pypi.org/project/pytest-django/).
107130

108-
#### Summary
109-
| Criterion | Django | Flask |
110-
| -- | -- | -- |
111-
| ORM | Tightly integrated; easy to use |
112-
| Templating | Django template engine or Jinja | -- |
113-
| View logic | Lots of abstractions for avoiding boilerplate | -- |
114-
| Authentication | django.contrib.auth + oidc library | -- |
115-
| Learning curve | Easy to get started with | -- |
116-
| Code structure | Encourages seperation of concerns | -- |
117-
| Security | Built in security features and guidance | -- |
118-
| Testing | Good testing tools included | -- |
131+
Flask also comes with a [test client](https://flask.palletsprojects.com/en/stable/testing/).
119132

120-
### Outcome
133+
#### CLI
134+
135+
Flask has a basic CLI tool that allows for running a dev server, displaying app routes, and running a shell in the app context. All other convenience commands would need to be implemented manually.
136+
137+
Django has the manage utility for running administrative tasks, including running a server, migrations, an application shell, inspecting the database schema, etc. One notable absence is printing out app routes.
121138

122-
TBD.
139+
#### Summary of differences
123140

124-
This decision will be relatively hard to move away from once we start building out the app, which is why we've spiked out two options to compare side by side.
141+
The following is the minimum set of concerns that would require us to hand-roll our own setup under Flask (and for which Django already provides a solution):
125142

126-
### Rationale
143+
- Project structure
144+
- ORM / database setup
145+
- Form rendering
146+
- Security protections comparable to the built-in Django protections
147+
- Helper scripts for common tasks
148+
149+
### Outcome
127150

128-
Provide a rationale for the decision that is based on weighing the options to ensure that the same questions are not going to be asked again and again unless the decision needs to be superseded.
151+
We decided to use Django on the basis that:
129152

130-
For non-trivial decisions a comparison table can be useful for the reviewer. Decision criteria down one side, options across the top. You'll likely find decision criteria come from the Drivers section above. Effort can be an important driving factor. You may have an intuitive feel for this, but reviewers will not. T-shirt sizing the effort for each option may help communicate.
153+
- The above represents a sizable amount of time and effort recreating things that are already available in Django
154+
- It would involve the use of various Flask extensions in varying states of maintenance, as opposed to a single, well-maintained framework
155+
- The security protections in particular are an area where it would be preferable not to roll our own, given the potential for leaving attack vectors open
131156

132157
## Consequences
133-
(If we pick Django...)
134158

135159
Engineers on the team will need to learn Django. Some suggested learning resources:
136160
- [Django documentation and first steps guide](https://docs.djangoproject.com/en/5.1/)

0 commit comments

Comments
 (0)