Skip to content

Commit 023eeff

Browse files
authored
Tidying, grammar, and minor clarifications (#13)
* Tidying, grammar, and minor clarifications * Changelog for 1.2.1 * Update README for 1.2.1
1 parent 829ffd3 commit 023eeff

File tree

7 files changed

+125
-37
lines changed

7 files changed

+125
-37
lines changed

CHANGELOG.md

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
1.2.1 | 25-09-2019
2+
================
3+
4+
- Fixed a lot of grammar issues.
5+
- Fixed some styling stuff to make things easier to read.
6+
- A note on shall methods / pass-through methods code-smell.
7+
- A note on wording.
8+
9+
Contributors:
10+
11+
- Paul Hallett (Author)
12+
113
1.2 | 10-06-2019
214
================
315

@@ -31,4 +43,4 @@ Initial version
3143

3244
Contributors:
3345

34-
- Paul Hallett (Author)
46+
- Paul Hallett (Author)

README.md

+6-5
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
# Django API Domains
44
_Style guides for the API age_
55

6-
| Version | Author(s) | Date |
7-
| ------------------------------------------------------------------- |-------------------------------------------|------------|
8-
| [1.2](https://github.com/phalt/django-api-domains/releases/tag/1.2) | Paul Hallett paulandrewhallett@gmail.com | 10-06-2019 |
9-
| [1.1](https://github.com/phalt/django-api-domains/releases/tag/1.1) | Paul Hallett paulandrewhallett@gmail.com | 09-04-2019 |
10-
| [1.0](https://github.com/phalt/django-api-domains/releases/tag/1.0) | Paul Hallett paulandrewhallett@gmail.com | 01-02-2019 |
6+
| Version | Author(s) | Date |
7+
| ----------------------------------------------------------------------- |-------------------------------------------|------------|
8+
| [1.2.1](https://github.com/phalt/django-api-domains/releases/tag/1.2.1) | Paul Hallett paulandrewhallett@gmail.com | 25-09-2019 |
9+
| [1.2](https://github.com/phalt/django-api-domains/releases/tag/1.2) | Paul Hallett paulandrewhallett@gmail.com | 10-06-2019 |
10+
| [1.1](https://github.com/phalt/django-api-domains/releases/tag/1.1) | Paul Hallett paulandrewhallett@gmail.com | 09-04-2019 |
11+
| [1.0](https://github.com/phalt/django-api-domains/releases/tag/1.0) | Paul Hallett paulandrewhallett@gmail.com | 01-02-2019 |
1112

1213
## Introduction
1314

docs/README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ In order to overcome these problems, this styleguide tries to achieve the follow
1919

2020
## Current Version
2121

22-
**CURRENT VERSION: [VERSION 1.2](https://github.com/phalt/django-api-domains/releases/tag/1.2)**
22+
**CURRENT VERSION: [VERSION 1.2.1](https://github.com/phalt/django-api-domains/releases/tag/1.2.1)**
2323

2424
## Previous Versions
2525

2626
All previous versions can be found under the `docs/` folder when looking at a [specific tag](https://github.com/phalt/django-api-domains/releases).
2727

2828
## CHANGELOG
2929

30-
Please see [CHANGELOG.md](https://github.com/phalt/django-api-domains/blob/master/CHANGELOG.md)
30+
Please see [CHANGELOG.md](https://github.com/phalt/django-api-domains/blob/master/CHANGELOG.md)

docs/domains.md

+24-9
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,42 @@
11
# Domains
22

3-
A [domain](https://en.wikipedia.org/wiki/Domain_(software_engineering)) is a piece of software that provides distinct business value within the context of your application.
3+
A [domain](https://en.wikipedia.org/wiki/Domain_(software_engineering)) is a piece of software that provides a distinct **business** value for your application.
44

5-
Within the context of software, what this styleguide calls a `domain` is roughly an extension of what Django would call an `app`. Therefore a _business_ domain **should** have at least one distinct _software_ domain mirroring it.
5+
What this styleguide calls a `domain` is roughly an extension of what Django would call an `app`. Therefore a business domain **should** have at least one distinct software domain mirroring it.
66

7-
## Examples in this guide
7+
---
88

9-
The examples in this guide will talk about a `book shop` that must share details about books. This can be modelled as a _domain_ called `books`, and as a _software domain_ also called `books`.
9+
> ### Examples in this guide
1010
11-
We keep the key benefits of Django's `app` pattern - namely Django's [models](https://docs.djangoproject.com/en/2.1/topics/db/models/) to represent tables in a datastore, with an emphasis on **skinny models**. We also retain Django's ability to *package apps as installable components in other applications*. This allows domains to be easily migrated to different codebases or completely different projects.
11+
> The examples in this guide will talk about a `book shop` that shares details about books.
12+
> This can be modelled as a _domain_ called `books`, and as a _software domain_ also called `books`.
13+
14+
---
15+
16+
This guide tries to keep the key benefits of Django's `app` pattern - namely Django's [models](https://docs.djangoproject.com/en/2.1/topics/db/models/) to represent tables in a datastore, but with an emphasis on **skinny models**.
17+
18+
This guide also retain Django's ability to *package apps as installable components in other applications*. This allows domains to be easily migrated to different codebases or completely different projects.
1219

1320
## Domain rules
1421

15-
There are two high-level rules around domains:
22+
There are two major rules around domains:
1623

1724
1. You **should** split a domain if it becomes too big to work on.
1825

19-
As a rule of thumb is that a domain should allow between 4-6 developers (3 pairs) to comfortably work on it. If you find your developers being blocked by each other then it is time to consider splitting the domain or checking the software has not diverged too far from the styleguide.
26+
A domain should allow between 4-6 developers (3 pairs) to comfortably work on it. If you find your developers being blocked by each other then it is time to consider splitting the domain or checking the software has not diverged too far from the styleguide.
27+
28+
---
2029

2130
2. You **should** adhere to the styleguide patterns in this document in order to maintain strong bounded contexts between your domains.
2231

23-
This applies even in situations where you extract one domain into two domains to increase velocity, but they still have to maintain a dependency between each other. We have found that if you relax the bounded context between domains, the boundary will erode and you will lose the ability to work on them independent of each other.
32+
This applies even in situations where you extract one domain into two domains to increase velocity, but they still have to maintain a dependency between each other. We have found that if you relax the bounded context between domains, the boundary will erode and you will lose the ability to work on them independent of each other.
33+
34+
35+
---
36+
37+
> ### Protip
38+
> An example _software_ domain is provided in the same directory as this styleguide under [example_domain/](https://github.com/phalt/django-api-domains/tree/master/example_domain).
2439
25-
An example _software_ domain is provided in the same directory as this styleguide under [example_domain/](https://github.com/phalt/django-api-domains/tree/master/example_domain).
40+
---
2641

2742
Next, we will discuss the styleguide in detail.

docs/files.md

+11-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11

2+
### Examples on this page
3+
24
In the examples below we imagine a service with two domains - one for books, and one for authors. The abstraction between books and authors is only present to demonstrate the concepts in the styleguide. You could argue that Books and Authors can live in one domain. In our example **we also assume a book can only have one author.** It's a strange world.
35

46
## Models
57

6-
Models defines how a data model/ database table looks. This is a Django convention that remains mostly unchanged. The key difference here is that you use _skinny models_ - no complex functional logic should live here. In the past Django has recommended an [active record](https://docs.djangoproject.com/en/2.1/misc/design-philosophies/#models) style for it's models. In practice, we have found that this encourages developers to make `models.py` bloated and do too much - often binding the presentation and functional logic of a domain too tightly. This makes it very hard to have abstract presentations of the data in a domain. Putting all the logic in one place also makes it difficult to scale the number of developers working in this part of the codebase. See the _"Where should logic live?"_ section above for clarification.
8+
Models defines how a data model/ database table looks. This is a Django convention that remains mostly unchanged. The key difference here is that you use _skinny models_. No complex functional logic should live here.
9+
10+
In the past Django has recommended an [active record](https://docs.djangoproject.com/en/2.1/misc/design-philosophies/#models) style for it's models. In practice, we have found that this encourages developers to make `models.py` bloated and do too much - often binding the presentation and functional logic of a domain too tightly. This makes it very hard to have abstract presentations of the data in a domain. Putting all the logic in one place also makes it difficult to scale the number of developers working in this part of the codebase. See the [_"Which logic lives where?"_](/styleguide/#which-logic-lives-where) section for clarification.
711

812
A models.py file can look like:
913

@@ -29,12 +33,13 @@ class Book(models.Model):
2933
- Models **should** own informational logic related to them.
3034
- Models **can** have computed properties where it makes sense.
3135
- Models **must not** import services, interfaces, or apis from their own domain or other domains.
32-
- Table dependencies (such as ForeignKeys) **must not** exist across domains. Use a UUID field instead, and have your Services control the relationship between models. You **can** use ForeignKeys between tables in one domain. Be aware that this might hinder future refactoring.
36+
- Table dependencies (such as ForeignKeys) **must not** exist across domains. Use a UUID field instead, and have your Services control the relationship between models.
37+
- You **can** use ForeignKeys between tables in one domain. (But be aware that this might hinder future refactoring.)
3338

3439

3540
## APIs
3641

37-
APIs defines the External API interface for your domain. Anyone using the APIs defined here is called a _consumer_. The API can be either an HTTP API using [graphQL](https://github.com/graphql-python) or [REST](https://www.django-rest-framework.org/) for consumers over the web, or a software API for internal consumers. APIs is defined in `apis.py` which is agnostic to the implementation you chose, and you can even put more than one API in a domain. For example - you might want to wrap a graphQL API _and_ a REST API around your domain for different consumers.
42+
APIs defines the external API interface for your domain. Anyone using the APIs defined here is called a _consumer_. The API can be either an HTTP API using [GraphQL](https://github.com/graphql-python) or [REST](https://www.django-rest-framework.org/) for consumers over the web, or a software API for internal consumers. APIs is defined in `apis.py` which is agnostic to the implementation you choose, and you can even put more than one API in a domain. For example - you might want to wrap a GraphQL API _and_ a REST API around your domain for different consumers.
3843

3944
An apis.py file that defines a simple software API can look like:
4045

@@ -64,7 +69,7 @@ class BookAPI:
6469
- If you are using a class for your internal APIs, it **must** use the naming convention `MyDomainAPI`.
6570
- Internal functions in APIs **must** use type annotations.
6671
- Internal functions in APIs **must** use keyword arguments.
67-
- You **should** log API call functions.
72+
- You **should** log API function calls.
6873
- All data returned from APIs **must be** serializable.
6974
- APIs **must** talk to Services to get data.
7075
- APIs **must not** talk to Models directly.
@@ -78,6 +83,8 @@ Your domain may need to communicate with another domain. That domain can be in a
7883

7984
Consider interfaces.py like a mini _Anti-Corruption Layer_. Most of the time it won't change and it'll just pass on arguments to an API function. But when the other domain moves - say you extract it into it's own web service, your domain only needs to update the code in `interfaces.py` to reflect the change. No complex refactoring needed, woohoo!
8085

86+
It's worth noting that some guides would consider this implementation a code smell because it has the potential for creating shallow methods or pass-through methods. This is somewhat true, and leads us back to the [pragmatism](/using/#be-pragmatic) point in our guide. If you find your `interfaces.py` is redundant, then you probably don't need it. That said: **we recommend starting with it and removing it later**.
87+
8188
An interfaces.py may look like:
8289

8390
```python

docs/styleguide.md

+57-11
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,19 @@
77
* A domain **must** use the following file structure:
88

99
```
10-
apis.py - Public functions and access points, presentation logic.
11-
interfaces.py - Integrations with other domains or external services.
12-
models.py - Object models and storage, simple information logic.
13-
services.py - coordination and transactional logic.
10+
- apis.py - Public functions and access points, presentation logic.
11+
- interfaces.py - Integrations with other domains or external services.
12+
- models.py - Object models and storage, simple information logic.
13+
- services.py - coordination and transactional logic.
1414
```
1515

16-
In addition, any existing files from a standard Django app are still allowed, such as `urls.py`, `apps.py` and `migrations/*`. `Views.py` in [Django's pattern](https://docs.djangoproject.com/en/dev/#the-view-layer) is **explicitly not allowed** in this styleguide pattern as we only focus on API-based applications. Most logic that used to live in Django's `views.py` would now be separated into `apis.py` and `services.py`.
16+
In addition, any existing files from a standard Django app are still allowed, such as `urls.py`, `apps.py` and `migrations/*`.
1717

18-
* You **can** mask one of the required files as a directory for better file organisation. For example, you might want to split `apis.py` into this file structure:
18+
* `Views.py` in [Django's pattern](https://docs.djangoproject.com/en/dev/#the-view-layer) is **explicitly not allowed** in this styleguide.
19+
20+
We only focus on API-based applications. Most logic that used to live in Django's `views.py` would now be separated into APIs and Services.
21+
22+
* You **can** mask one of the required files as a directory for better file organisation. For example, you might want to split `apis.py` file into this structure:
1923

2024
```
2125
apis/
@@ -41,7 +45,7 @@ from domain.apis import Foo
4145

4246
This keeps namespaces tidy and does not leak domain details.
4347

44-
* A domain **does not** need to have all these files if it is not using them. For example - a domain that just coordinates API calls to other domains does not need to have `models.py` as it is probably not storing anything in a datastore.
48+
* A domain **does not** need to have all these files if it is not using them. For example - a domain that just coordinates API calls to other domains does not need to have Models as it is probably not storing anything in a datastore.
4549

4650
* A domain **can have** additional files when it makes sense (such as `utils.py` or `enums.py` or `serializers.py`) to separate out parts of the code that aren't covered by the styleguide pattern.
4751

@@ -62,8 +66,50 @@ With this ruling domains are easy to package and move around. When it comes time
6266

6367
## Which logic lives where?
6468

65-
It's common in programming to end up confused about what type of logic should live where - should it go in the `apis.py`, the `models.py`, or the `services.py`? There are many cases where it's difficult to decide, and the best advice is to **pick a pattern and stick to it**, but for simpler things, this guide emphasises the following:
69+
It's common in programming to end up confused about what type of logic should live where.
70+
71+
There are many cases where it's difficult to decide, and the best advice is to **pick a pattern and stick to it**, but for simpler things, this guide emphasises the following:
72+
73+
### APIs
74+
Logic about presentation.
75+
76+
---
77+
78+
> **If you ask:**
79+
> "Where should I show this data to the user?" or "Where do I define the API schema?"
80+
81+
---
82+
83+
84+
### Services
85+
Logic around coordination and transactions.
86+
87+
---
88+
89+
> **If you ask:**
90+
> "Where do I coordinate updating many models in one domain?" or "Where do I dispatch a single action out to other domains?"
91+
92+
---
93+
94+
95+
### Models
96+
Logic around information.
97+
98+
---
99+
100+
> **If you ask:**
101+
> "Where can I store this data?" or "Where can I do any post/pre-save actions?"
102+
103+
---
104+
105+
106+
### Interfaces
107+
108+
Logic handling the transformation of data from other domains.
109+
110+
---
111+
112+
> **If you ask:**
113+
> "Where shall I connect to another domain?" or "How do I change the data format for another domain?"
66114
67-
- `apis.py` - logic about presentation (Where should I show this data to the user? Where do I define the API schema?)
68-
- `services.py` - logic around coordination and transactions (Where do I coordinate updating many models in one domain? Where do I dispatch a single action out to other domains?)
69-
- `models.py` - logic around information (Where can I store this data? Where can I do any post/pre-save actions?) and derived information computed by already existing information
115+
---

docs/using.md

+12-5
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
11
# Pin against a version
22

3-
In software we often specify a version of a project to use.
3+
In software we often specify a version of a project to use. For example - `requests==1.2` - this says _"we want to use requests version 1.2"_.
44

5-
For example - `requests==1.2` - this says _"use requests version 1.2"_.
6-
7-
For this guide, we recommend the following picking a specific version and working against it. Just like software; future versions of this guide could change and that would mean conflicting issues with the styleguide in your projects.
5+
For this guide we recommend the following: picking a specific version and working against it. Just like software; future versions of this guide could change and that could cause conflicting issues with the styleguide in your project.
86

97
You can see the released versions [here](https://github.com/phalt/django-api-domains/releases) and by clicking on the tag icon you can view historical versions. The published version at [https://phalt.github.io/django-api-domains](https://phalt.github.io/django-api-domains) will always be the latest version.
108

119
# Be pragmatic
1210

1311
Don't blindly follow what other people have written down. Sometimes it might not be suitable for your own problem. Use this guide as inspiration and guidance, but make your own decisions with your team when you aren't satisfied with it.
1412

15-
Just make sure you keep track of any additional rules you create.
13+
---
14+
15+
> ### Protip
16+
> Keep track of any additional rules you make to keep your styleguide consistent!
17+
18+
---
19+
20+
# Wording in this guide
21+
22+
Sometimes this guide refers to the following areas in a domain: `interfaces`, `apis`, `services`, and `models`. You can assume any code examples are from the file name of the same area. E.g. - code examples under an `apis` heading is meant for the `apis.py` file.

0 commit comments

Comments
 (0)