-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy patharchitectural-decisions.xml
138 lines (111 loc) · 7.27 KB
/
architectural-decisions.xml
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
<?xml version="1.0" encoding="UTF-8"?>
<architecturalDecisions xmlns="https://architectural-decision.cspray.io/schema/architectural-decision.xsd">
<architecturalDecision id="ExplicitArchitecturalDecisionStatus" attribute="Cspray\ArchitecturalDecision\ArchitecturalDecisionRecords\ExplicitArchitecturalDecisionStatus">
<date>2022-07-19</date>
<status>Accepted</status>
<contents><![CDATA[# Explicit Architectural Decision Status
An explicit status should be provided for each ArchitecturalDecisionRecord instead of relying on an implicit status,
e.g. the presence of the ADR in your project's main branch.
## Problem Description
As a developer I would like for the inclusion of my ADR in a default branch, or some other by-convention mechanism, to
implicitly determine that ADR's status.
## Decision
Some libraries may be able to take advantage of an implicit, by-convention approach to statuses. If possible, it is
encouraged to do so by implementing your own abstract ArchitecturalDecisionRecord that defines the statuses for all
decisions according to your conventions.
Other projects might require more complete processes around the status of a decision. For example, a 'Draft - Working'
status for complex decisions to see how they impact the codebase and team over time. Previously 'Accepted' decisions
could become 'Deprecated' or 'Rejected' outright. In those situations leaving the decision in place, while removing
annotations using the Attribute, could be the preferred approach. This way, why the previously accepted decision is
no longer valid stays in the history of the codebase instead of being removed. Or, perhaps you use the 'Deprecated'
status of the decision to find code locations that are in need of refactoring.
Requiring an explicit status enables both workflows; having an implicit status is still supported by providing your
own implementation logic and explicit statuses are also supported. Only having an implicit status would not allow
for both workflows.]]></contents>
<codeAnnotations>
<codeAnnotation>
<class>Cspray\ArchitecturalDecision\ArchitecturalDecisionRecord</class>
</codeAnnotation>
</codeAnnotations>
<meta/>
</architecturalDecision>
<architecturalDecision id="UsingAttributesForArchitecturalDecisions" attribute="Cspray\ArchitecturalDecision\ArchitecturalDecisionRecords\UsingAttributesForArchitecturalDecisions">
<date>2022-07-19</date>
<status>Accepted</status>
<contents><![CDATA[# Use Attributes for Architectural Decisions
Architectural Decision Records (ADR) can be useful in determining why a piece of software is the way it is. While
these type of documents can live anywhere, an Attribute in your codebase can be a good place to store this info.
For more information, please check out the README in this repo or at https://github.com/cspray/architectural-decision]]></contents>
<codeAnnotations>
<codeAnnotation>
<class>Cspray\ArchitecturalDecision\ArchitecturalDecisionRecord</class>
</codeAnnotation>
</codeAnnotations>
<meta/>
</architecturalDecision>
<architecturalDecision id="PreferCustomArchitecturalDecisionOverGeneric" attribute="Cspray\ArchitecturalDecision\ArchitecturalDecisionRecords\PreferCustomArchitecturalDecisionOverGeneric">
<date>2022-07-19</date>
<status>Accepted</status>
<contents><![CDATA[# Prefer Custom ArchitecturalDecisionRecord Over Generic
It is preferred to create a custom Attribute per decision over implementing a generic Attribute to handle many
decisions.
## Problem Description
As a developer using Architectural Decision I would like to use a generic Attribute to define an Architectural Decision
Record instead of having to implement a custom Attribute unique to each decision.
## Decision
One of the earliest decisions in the design of this library was to have the supported use case be a custom Attribute
per ADR and to discourage the use of a generic attribute, e.g. #[ADR('Title', '2022-07-19', 'Reasoning')]. On the
surface a generic Attribute has some advantages, there are also drawbacks that make them a poor solution for what
this library is trying to accomplish. Specifically, I identify 3 problems with the generic Attribute approach.
1. Generic Attributes increase the likelihood that you'll have to repeat information and that errors/typos are
introduced. If you use a generic Attribute then every place that you use that Attribute in your codebase must also
include all the relevant information. This could easily turn problematic if the same ADR has different state in each
place it is attributed. The decision details should be documented in one, canonical place and not spread across your
codebase.
2. Generic Attributes do not facilitate static analysis. Having an explicit type for each decision allows for static
analysis tools to more easily infer information about each use of the Attribute. A generic Attribute for all decisions
would lose the ability to infer this information as you're now much more dependent on the value of the Attribute
instead of the type to infer which decision it refers to.
3. This library cannot rely strictly on Reflection to retrieve an Attribute instance and must be able to construct
an Attribute type without it being used in the codebase. To understand this restriction let's look at how you would
typically get an instance of an Attribute that's been annotated on a class. Given the following code example:
<code>
#[Attribute]
class SomeAttribute {
public function __construct(
public readonly string $foo,
public readonly string $bar
) {}
}
#[SomeAttribute('baz', 'qux')]
class SomeClass {
}
</code>
To retrieve the instance of SomeAttribute that has 'baz' and 'qux' stored in the attribute's state we'd have to run
code that resembles the following:
<code>
$reflection = new ReflectionClass(SomeClass::class);
$reflectionAttribute = $reflection->getAttributes(SomeAttribute::class)[0];
$instance = $reflectionAttribute->newInstance();
echo $instance->foo; // baz
echo $instance->bar; // qux
</code>
For traditional Attribute usage this is perfectly sufficient. However, since this library is using the Attribute in a
non-traditional sense we can't always rely on being able to execute this code. For example, if you write an ADR as an
Attribute but then never annotate anything in your codebase with it. In this scenario your ADR would not be included.
Not including an ADR because it hasn't been used in your codebase is considered a bug. To prevent this from happening
we must be able to construct a Cspray\ArchitecturalDecision\ArchitecturalDecisionRecord instance without using the
Reflection API.
It is possible to resolve this problem by allowing a factory construct the ArchitecturalDecisionRecord instances, but
with additional complexity and not solving either of the 2 primary problems with generic Attributes. Instead of doing
that it is encouraged to always implement a custom Attribute per decision. If you have a compelling use case that can
only be solved by having control of the Attribute instance is created please submit an Issue at
https://github.com/cspray/architectural-decision.]]></contents>
<codeAnnotations>
<codeAnnotation>
<class>Cspray\ArchitecturalDecision\ArchitecturalDecisionRecord</class>
</codeAnnotation>
</codeAnnotations>
<meta/>
</architecturalDecision>
</architecturalDecisions>