New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Architecture Paper #7

Open
wants to merge 11 commits into
base: master
from

Conversation

Projects
None yet
8 participants
@ahawkins
Owner

ahawkins commented Nov 3, 2013

Instructions for Reviewers: First of all, thanks so much for doing this! I’m really proud of this paper and want it to be my best work yet. This means that I’m approaching this from all angles. I’d appreciate your feedback on these things specifically:

  • Do you agree with the technical assertions in this paper?
  • How are the transitions between each paragraph and sections (aka how
    does it read?)
  • How does the perspective come off? I’ve specifically tried to avoid
    using the word “I”. I don’t want the paper to come off as an opinion
    piece but a presentation of technical knowledge gained by
    experience.
  • Do the code and test examples flow?
  • What do you think should be removed?
  • How can I make this more professional?
  • And whatever else you can think of :)
  • Leave all your comments in this PR
  • Also expect typos and things like that. I can only proofread my own writing so many times :)

Sections not implemented yet:

  • migration strategies
  • conclusion
  • RadiumCRM case study
## Effective Design & Technical Investment
Effective software design focuses on enforcing boundaries and applying
design patterns. Maintainable systems have boundaries in the right

This comment has been minimized.

@avdi

avdi Nov 11, 2013

That depends on what you're designing for - enforcing boundaries is good strategy for long-term maintainable software design; we might say "robust" design. "Effective" is a bit broad.

I disagree with putting design patterns at this level of importance, and I say this as a vocal patterns advocate. Patterns are a means to an end. They aren't a focus of good design, they are a tool employed in its pursuit.

@avdi

avdi Nov 11, 2013

That depends on what you're designing for - enforcing boundaries is good strategy for long-term maintainable software design; we might say "robust" design. "Effective" is a bit broad.

I disagree with putting design patterns at this level of importance, and I say this as a vocal patterns advocate. Patterns are a means to an end. They aren't a focus of good design, they are a tool employed in its pursuit.

Show outdated Hide outdated wip/untitled.md Outdated
Show outdated Hide outdated wip/untitled.md Outdated
years have a strong lasting impact. The signs of excellent parenting
live on and people grow into well adjusted individuals. Horrible or
abusive parenting often leave scars for life which are difficult or
impossible to heal without serious effort. This paper is about making

This comment has been minimized.

@avdi

avdi Nov 11, 2013

I'm not sure it's a good idea to call to mind "horrible or abusive" parents in a technical paper - that may be a real pain point for some people. It's not a bad metaphor, but it may misfire.

@avdi

avdi Nov 11, 2013

I'm not sure it's a good idea to call to mind "horrible or abusive" parents in a technical paper - that may be a real pain point for some people. It's not a bad metaphor, but it may misfire.

This comment has been minimized.

@JoshTGreenwood

JoshTGreenwood Dec 4, 2013

I was about to write the same thing as @avdi. I would cut the 'Horrible or...' sentence to avoid the issue altogether. I believe it flows just fine without it.

@JoshTGreenwood

JoshTGreenwood Dec 4, 2013

I was about to write the same thing as @avdi. I would cut the 'Horrible or...' sentence to avoid the issue altogether. I believe it flows just fine without it.

Show outdated Hide outdated wip/untitled.md Outdated
customer instance. The instance is then passed to the use case for
transformative action. Form objects encapsulate this responsibility.
Form objects represent the first boundary.

This comment has been minimized.

@avdi

avdi Nov 11, 2013

Your sentences are delightfully short, but your paragraphs are rather long.

@avdi

avdi Nov 11, 2013

Your sentences are delightfully short, but your paragraphs are rather long.

unique ID. The system uses this information to look up the existing
customer instance. The instance is then passed to the use case for
transformative action. Form objects encapsulate this responsibility.
Form objects represent the first boundary.

This comment has been minimized.

@avdi

avdi Nov 11, 2013

Again, treating a use case as a programmatic entity rather than an entry in a requirements document or a note on a 3x5 card. If you're planning on introducing Use Cases as a first-class entity in your designs, as e.g. the DCI folks tend to do, that's fine. But you might want to make that introduction before talking about use cases in that context. Otherwise folks like me who think about use cases in terms of bits of paper or entries in Pivotal Tracker may experience some cognitive dissonance.

@avdi

avdi Nov 11, 2013

Again, treating a use case as a programmatic entity rather than an entry in a requirements document or a note on a 3x5 card. If you're planning on introducing Use Cases as a first-class entity in your designs, as e.g. the DCI folks tend to do, that's fine. But you might want to make that introduction before talking about use cases in that context. Otherwise folks like me who think about use cases in terms of bits of paper or entries in Pivotal Tracker may experience some cognitive dissonance.

Boundaries are fundamental concepts in software architecture. They
determine area of responsibilities and knowledge. Dependencies also
point in one direction. Code cannot reach over the boundary and

This comment has been minimized.

@avdi

avdi Nov 11, 2013

"Dependencies also point in one direction" -- I don't understand how this sentence references the previous one.

@avdi

avdi Nov 11, 2013

"Dependencies also point in one direction" -- I don't understand how this sentence references the previous one.

Unfortunately our software is not as smart. Software engineers must
respect this boundary and actively fight to maintain it’s integrity.
Once the use case has the require input it coordinates interaction

This comment has been minimized.

@avdi

avdi Nov 11, 2013

s/require/required/ - noting because that one could slip by spellcheck

@avdi

avdi Nov 11, 2013

s/require/required/ - noting because that one could slip by spellcheck

implementation details. This implementation perfectly describes the
repository pattern. Matin Fowler describes the repository pattern in
“Patterns of Enterprise Architecture”.

This comment has been minimized.

@avdi

avdi Nov 11, 2013

We've gone through a lot of high level concepts at a breakneck pace without anything concrete. I am desperate for code at this point!

@avdi

avdi Nov 11, 2013

We've gone through a lot of high level concepts at a breakneck pace without anything concrete. I am desperate for code at this point!

people were able to implement their own persistence mechanisms very
quickly without changing any part of the broader application. Proper
concern separation empowers rapid localized changes.

This comment has been minimized.

@avdi

avdi Nov 11, 2013

Some random notes on this stuff... I've been studiously following PoeAA in some work I've been doing on the RubyTapas site, and it has had a wonderfully salutary effect on the simplicity of the code. I heartily recommend the approach.

I want to emphasize that each piece of the PoEAA puzzle has major payoffs. For instance, in my work, simply applying Gateway was a major win. Then I layered Mapper on top (e.g. EpisodeMapper, VideoMapper...), and that too was a big win. Then I added a caching layer in between the two, which turned out to be dead simple because of the separation between gateways and mappers. Then I added an Identity Map. And so on.

I haven't even reached the need for Repository and Query yet, and I've already experienced major design epiphanies. If there's a point to all this, it's this: consider not glossing over the building blocks that go underneath Repository. Not every app needs a repository (some don't even need Mapper). And every single layer of the cake, if approached mindfully and intentionally, can bring serious benefits.

@avdi

avdi Nov 11, 2013

Some random notes on this stuff... I've been studiously following PoeAA in some work I've been doing on the RubyTapas site, and it has had a wonderfully salutary effect on the simplicity of the code. I heartily recommend the approach.

I want to emphasize that each piece of the PoEAA puzzle has major payoffs. For instance, in my work, simply applying Gateway was a major win. Then I layered Mapper on top (e.g. EpisodeMapper, VideoMapper...), and that too was a big win. Then I added a caching layer in between the two, which turned out to be dead simple because of the separation between gateways and mappers. Then I added an Identity Map. And so on.

I haven't even reached the need for Repository and Query yet, and I've already experienced major design epiphanies. If there's a point to all this, it's this: consider not glossing over the building blocks that go underneath Repository. Not every app needs a repository (some don't even need Mapper). And every single layer of the cake, if approached mindfully and intentionally, can bring serious benefits.

@name = name
end
end

This comment has been minimized.

@avdi

avdi Nov 11, 2013

I usually use Struct for stuff like this, but I'm weird that way.

@avdi

avdi Nov 11, 2013

I usually use Struct for stuff like this, but I'm weird that way.

This comment has been minimized.

@ka8725

ka8725 Jul 15, 2014

I think that Struct does something different. For example, it also defines writers for fields, but sometimes you don't have them at all. Current example is better because it doesn't require to introduce Struct (some readers may not be aware of this stuff).

@ka8725

ka8725 Jul 15, 2014

I think that Struct does something different. For example, it also defines writers for fields, but sometimes you don't have them at all. Current example is better because it doesn't require to introduce Struct (some readers may not be aware of this stuff).

end
end
This may seem like an anti-pattern. It is a bit unsettling, but very

This comment has been minimized.

@avdi

avdi Nov 11, 2013

I'm going to go against character a bit and say that this seems like a mild YAGNI violation. I often amuse myself with extra-complete implementations like this, but I'd have a hard time justifying it in a commercial project until it appeared to be needed.

OTOH, the moment I saw two separate lines of code parsing time in order to satisfy this setter method, I'd write the tests and code that would enable me to move the duplicated parse into the TodoForm class.

@avdi

avdi Nov 11, 2013

I'm going to go against character a bit and say that this seems like a mild YAGNI violation. I often amuse myself with extra-complete implementations like this, but I'd have a hard time justifying it in a commercial project until it appeared to be needed.

OTOH, the moment I saw two separate lines of code parsing time in order to satisfy this setter method, I'd write the tests and code that would enable me to move the duplicated parse into the TodoForm class.

form.due_date = :bar
end
end

This comment has been minimized.

@avdi

avdi Nov 11, 2013

For whatever it's worth, I don't bother writing tests for these sorts of basic preconditions. I just throw the preconditions in and move on.

@avdi

avdi Nov 11, 2013

For whatever it's worth, I don't bother writing tests for these sorts of basic preconditions. I just throw the preconditions in and move on.

The model needs a method that does logic and publishes events. It also
needs an interface to attach observers. The ruby standard library
provides the `Observable` module. There is also some miscallenous

This comment has been minimized.

@avdi

avdi Nov 11, 2013

To be blunt, the observer library kind of sucks. I tend to write this stuff myself because I don't like jamming all event notifications into a single generic #notify method. See the various "listeners" in GOOS for inspiration.

@avdi

avdi Nov 11, 2013

To be blunt, the observer library kind of sucks. I tend to write this stuff myself because I don't like jamming all event notifications into a single generic #notify method. See the various "listeners" in GOOS for inspiration.

them to other entities in the system. This keeps models singularly
focused on data.
Now the code fulfills all the happy path requirements. What happen’s

This comment has been minimized.

@avdi

avdi Nov 11, 2013

s/happen's/happens/

@avdi

avdi Nov 11, 2013

s/happen's/happens/

before the use case can continue. Implementing validation logic is
surprisingly complex. The implementation depends on how
responsibilities are defined and how input is collected.

This comment has been minimized.

@avdi

avdi Nov 11, 2013

I gotta call it a day at this point, but I'll try and come back to it when I have some time. Thanks for the opportunity!

@avdi

avdi Nov 11, 2013

I gotta call it a day at this point, but I'll try and come back to it when I have some time. Thanks for the opportunity!

This comment has been minimized.

@ahawkins

ahawkins Nov 11, 2013

Owner

Awesome! Thanks a bunch man for all the constructive comments. Your points about layering design patterns on top of each other was something I had in my head but could not put into words. Your comments are very motivating :)

@ahawkins

ahawkins Nov 11, 2013

Owner

Awesome! Thanks a bunch man for all the constructive comments. Your points about layering design patterns on top of each other was something I had in my head but could not put into words. Your comments are very motivating :)

Show outdated Hide outdated wip/untitled.md Outdated
Show outdated Hide outdated wip/untitled.md Outdated
Show outdated Hide outdated wip/untitled.md Outdated
Show outdated Hide outdated wip/untitled.md Outdated
Show outdated Hide outdated wip/untitled.md Outdated
*Possible Sandi Metz quote*. This paper demonstrates an object
arrangement that exemplifies all the important characteristics of a
maintainable and extendable application. Creating good software
requires heavy focus on its core functionality. We call know them as

This comment has been minimized.

@JoshTGreenwood

JoshTGreenwood Dec 4, 2013

Instead of using 'we', perhaps change the sentence to "This paper refers to that functionality as use cases."

@JoshTGreenwood

JoshTGreenwood Dec 4, 2013

Instead of using 'we', perhaps change the sentence to "This paper refers to that functionality as use cases."

## Use Cases: Heart of the System
A use case describes something a system does. It is a unit of work. A
CRM (Customer Relationship Management) system has use cases like

This comment has been minimized.

@JoshTGreenwood

JoshTGreenwood Dec 4, 2013

I would change 'has' to 'might have'. I would also change 'like' to 'such as'.

@JoshTGreenwood

JoshTGreenwood Dec 4, 2013

I would change 'has' to 'might have'. I would also change 'like' to 'such as'.

systems's core. Use cases have alternate flows and are often composed
into more complex flows. A CRM may want to add a customer then contact
them. This is possible when implemented correctly and down right
painful when not. Use cases are usually not straight forward. They

This comment has been minimized.

@JoshTGreenwood

JoshTGreenwood Dec 4, 2013

Use cases are typically straight forward, I think you mean their implementation is not. Maybe combine this sentence with the next: "The implementation of use cases is rarely straight forward because many of them may interact with each other."

@JoshTGreenwood

JoshTGreenwood Dec 4, 2013

Use cases are typically straight forward, I think you mean their implementation is not. Maybe combine this sentence with the next: "The implementation of use cases is rarely straight forward because many of them may interact with each other."

@ahawkins

This comment has been minimized.

Show comment
Hide comment
@ahawkins

ahawkins Dec 5, 2013

Owner

@JoshTGreenwood Thanks for all your comments man!

Owner

ahawkins commented Dec 5, 2013

@JoshTGreenwood Thanks for all your comments man!

@saturnflyer

This comment has been minimized.

Show comment
Hide comment
@saturnflyer

saturnflyer Dec 12, 2013

A use case, I'd argue, has a user/business goal.
Use cases will be as straightforward (or not) as your application. There's no inherent requirement that they interact with many other entities. Is 2 considered many? A use case could involve as little as 2 actors.

It seems like sentences like:

A use cases takes in some form of input and takes appropriate action. The input is examined and some records are created or modifies.

have a particular thing in mind which is not inherent to a use case. I think there's some mingling of the idea of a use case (which describes a system) and some object inside of a system. A Context object in a DCI architecture has the job of encapsulating the interactions among the roles of a system. Often this is confused with a service object.

I agree that this should have more about the domain which would be relevant to the roles and their goals in a use case.

saturnflyer commented on wip/untitled.md in fe78db8 Dec 12, 2013

A use case, I'd argue, has a user/business goal.
Use cases will be as straightforward (or not) as your application. There's no inherent requirement that they interact with many other entities. Is 2 considered many? A use case could involve as little as 2 actors.

It seems like sentences like:

A use cases takes in some form of input and takes appropriate action. The input is examined and some records are created or modifies.

have a particular thing in mind which is not inherent to a use case. I think there's some mingling of the idea of a use case (which describes a system) and some object inside of a system. A Context object in a DCI architecture has the job of encapsulating the interactions among the roles of a system. Often this is confused with a service object.

I agree that this should have more about the domain which would be relevant to the roles and their goals in a use case.

@saturnflyer

This comment has been minimized.

Show comment
Hide comment
@saturnflyer

saturnflyer Dec 12, 2013

This reads sort of like you are describing a Context object from DCI. In both use case terminology (from Cockburn) and DCI terminology there isn't required input, but a user action which triggers the use case or interaction of objects. But the "required input" makes me think this is something else (unless you mean trigger).

saturnflyer commented on wip/untitled.md in fe78db8 Dec 12, 2013

This reads sort of like you are describing a Context object from DCI. In both use case terminology (from Cockburn) and DCI terminology there isn't required input, but a user action which triggers the use case or interaction of objects. But the "required input" makes me think this is something else (unless you mean trigger).

This is hard change to make because it fundamentally reverses how
application’s are commonly structured. Uncle Bob speaks about his
personal experience in his talk “Architecture, The Lost Years”. He
provides a case study about INSERT PROJECT NAME HERE. He says that

This comment has been minimized.

@rab

rab Mar 6, 2014

s/INSERT PROJECT NAME HERE/FitNesse/

@rab

rab Mar 6, 2014

s/INSERT PROJECT NAME HERE/FitNesse/

TodoRepo.save self
if was_new_record || (!was_new_record && assigned_to_changed?)

This comment has been minimized.

@rab

rab Mar 6, 2014

This condition need only be:

    if was_new_record || assigned_to_changed?

The extra test for !was_new_record can never fail because the || would have never evaluated the right-hand-side unless was_new_record was falsey to start.

@rab

rab Mar 6, 2014

This condition need only be:

    if was_new_record || assigned_to_changed?

The extra test for !was_new_record can never fail because the || would have never evaluated the right-hand-side unless was_new_record was falsey to start.

HTTP. The request can only reference an identifier (such as a unique
id). The application sends the user's name. The form must implement
this behavior. The form already contains an example of how to parse
times. Astute readers will not that the time is sent in ISO8601

This comment has been minimized.

@rab

rab Mar 6, 2014

s/will not that/will note that/

@rab

rab Mar 6, 2014

s/will not that/will note that/

So far this paper has espoused boundaries and proper responsibility
separation. Design patterns are used to arrange code the best way. The
example does not any gems (besides Sinatra). This is an explicit

This comment has been minimized.

@rab

rab Mar 6, 2014

s/does not any gems/does not use any gems/

@rab

rab Mar 6, 2014

s/does not any gems/does not use any gems/

The view layer is a potential minefield in every delivery mechanism.
It is easy for logic to enter templates. Over time templates become
the biggest technical debt source in the application. This must be
actively fought against. Logic enters templates faster than any other

This comment has been minimized.

@rab

rab Mar 6, 2014

The repetition of "encourage" seems a bit odd here. Perhaps something like:

Rail's design does not promote technical investment. It enables technical debt and in most cases actually encourages it.

@rab

rab Mar 6, 2014

The repetition of "encourage" seems a bit odd here. Perhaps something like:

Rail's design does not promote technical investment. It enables technical debt and in most cases actually encourages it.

The view layer is a potential minefield in every delivery mechanism.
It is easy for logic to enter templates. Over time templates become
the biggest technical debt source in the application. This must be
actively fought against. Logic enters templates faster than any other

This comment has been minimized.

@rab

rab Mar 6, 2014

Rather than "Overtime the"

Over time, the

@rab

rab Mar 6, 2014

Rather than "Overtime the"

Over time, the

Fowler does not mention how they are persisted and that is very
important. A boundary is created between how the data is accessed and
how it is stored. This is very useful in practice. First of all it
allows the two abstractions to very independently. The public

This comment has been minimized.

@dclausen

dclausen Apr 9, 2014

s/very/vary

@dclausen

dclausen Apr 9, 2014

s/very/vary

This use case is taken directly from a CRM. The example revolves
around creating todos. A todo is a description and a due date. Todos
may optionally be assigned to users. Descrition and due date are

This comment has been minimized.

@dclausen

dclausen Apr 10, 2014

s/Descrition/Description/

@dclausen

dclausen Apr 10, 2014

s/Descrition/Description/

Models encapsulate data. Use cases manipulate and coordinate their
interactions. This means the models do not contain any related
behavior. A model only knows about itself and it's data. Expressing
models is easy in Ruby. Delcare a class and add the proper accessors.

This comment has been minimized.

@avsej

avsej Jul 16, 2014

s/Delcare/Declare

@avsej

avsej Jul 16, 2014

s/Delcare/Declare

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment