Software Design
Axioms
- software design is not a solid stuff
- designing software architecture never ends
- it is changing over development
- the primary purpose of architecture is to support the life cycle of the system
- good architecture makes the system easy to understand, easy to develop, easy to maintain, and easy to deploy
- the ultimate goal is to minimize the lifetime cost of the system and to maxmize programmer productivity
- the architecture of the system should elevate the use cases, the features, and the required behaviours of the system to first-class entities that are visible landmarks for the developers
- good architecture centered on use cases so that architects can safely describe the structures that support those use cases without commiting to frameworks, tools, and environments
Software Architecture
- Structure
- type of architecture style
- microservice
- layered
- microkernel
- type of architecture style
- Architecture Charactaristics
- success criteria of a system
- availability
- reliability
- testability
- scalability
- security
- agility
- fault tolerance
- elasticity
- receverability
- performance
- deployablility
- learnability
- success criteria of a system
- Architecture Decision
- rules for how a system should be constructed
- Design Principles
- a guideline
Symptoms of Complexity
- change amplification
- cognitive load
- unknown unknown
Causes of Complexity
- dependendies
- obscurity
Modular Design
- goal of modular design is moinimize the dependencies between modules
- module consists of interface and implementation
Interfaces
- any information a developer needs to know in order to use a module
- e.g.
- signiture
- name
- parameters
- returns
- comments, documents
- signiture
- if users need to read the code of a method in order to use it, then there is no abstraction
- e.g.
- more important for a module to have a simple interface than a simple implementation
Implementations
- reduce the number of places where exceptions have to be handled
- define errors out of existence
Performance
- before attempting to improve performance, measure the system's existing behaviour
Domain
- subject area to which users applies the program
- subject area of the problem we solve using software
Model
- simplification
- abstraction
- filter out extraneous details
- effective modeling
- knowledge crunching
- cultivating a language based on the model
- Ubiquitous Language
- changes in the Ubiquitous Language are changes to the model
- Ubiquitous Language
- iteration
- software development is all design
- don't just model reality (e.g. actual manual operations that will be automated by software/system) as is
- model them from the perspective of software/system too
SOLID Principles
suggest how to arrange functions and data structures into classes, and how those classes should be interconnected to design the mid-level (module level) software architectures that
- tolerate change
- are easy to undarstand
- are reusable in many software systems
SRP; Single Responsibility Principle
- a module should be responsible to one, and only one, actor
- actor: group of user or stakeholder who wants the system changed in the same way
- a moule should NOT have multiple reasons to change
OCP; Open-Closed Principle
- a software architect should be open for extension but closed for modification
- if SRP is obeyed, a feature extension won't affect to modules for other features
- associated with CCP
LSP; Liskov Substitution Principle
- polymorphism
ISP; Interface Segregation Principle
- avoid depending on things that they don't use
- have clear abstraction/concept of the module
DIP; Dependency Inversion Principle
- implement interfaces for the high-level module which uses lower-level modules
- lower-level modules should NOT know the implementation details of higher-level modules
- have lower-level modules obey the interfaces
- now we can have inverted dependency from lower-level to higher-level without knowledge about implemetation details of higher-level module
Components
Component-level Principles
REP; Reuse/Release Equivalence Principle
- the granule of reuse is the granule of release
CCP; Common Closure Principle
- component-level SRP
- a component should NOT have multiple reasons to change
CRP; Common Reuse Principle
- component-level ISP
Component Dependencies
- component structure cannot be designed from the top down
- component dependencies graph is created for the first time if it reached the time dependency management was necessary
- component dependencies graphs is for mapping to buildability and maintainability of the application; not for describing the function of the application
ADP; Asynclic Dependencies Principle
- allow no cycles in the component dependency graph
SDP; Stable Dependencies Principle
- depend in the direction of stability
- ensure that modules that are intended to be easy to change are not depended on by modules that are harder to change
SAP; Stable Abstraction Principle
- a component should be as abstract as it is stable
- SDP + SAP -> dependencies run in the direction of abstraction
Business Rules
- Critical Business Rules
- make or save the business money
- some of them are purelly irrespective of whether they were implemented on a computer
- Entity
- Critical Business Data
- data required by critical business rules
- generalized concept; not application-specific
- Critical Business Data
- Use Cases
- define application-specific business rules
- automated operation to make or save the business money
- at lower-level than Entities since Use Cases depend on the application/system; not generalized as much as Entities
- e.g. apply an estimation (validation) to user info for loan then create Customer entity if acceptable
- have input and ouput
- but should not depend on how input and output are delivered
- e.g. HTTP request and response, HTML, SQL
- but should not depend on how input and output are delivered
- define application-specific business rules
Humble Objects
- separate behaviours into tastables and non-testables
- from the uesr's point of view, an object is simply a set of operations; an object represent behaviours
- becaues users cannot see private fields
Layered Architecture
- User Interface (or Presentation Layer)
- Application Layer
- services
- not contain business rules or knowledge
- not have state reflecting business situation
- only coordinates tasks and delegates work to collaborations of domain objects in the next layer down
- Domain Layer (or Model Layer)
- business situation
- business rules
- control and use state reflecting the business situation
- Infrastructure Layer
- ORM
Model-Driven Design
Components
Entities
- distinguished by its identity
- guaranteed the uniqueness
- need to maintain the life cycle
- e.g. user
Value Objects
- representing a descriptive aspect of the domain with no conceptual identity
- representing elements of the design that we care about only for what they are, not who or which they are
- immutable
- disposable
- e.g.
- attributes of an Entity
- used as parameters in messages between objects
Services
- overview
- an operation offered as an interface that stands alone in the model, without encapsulating state, as Entities and Value Objects do
- operation names should come from the Ubiquitous Language or be introduced into it
- parameters and results should be domain objects
- an operation offered as an interface that stands alone in the model, without encapsulating state, as Entities and Value Objects do
- characteristics
- the operation relates to a domain concept that is not a natural part of an Entity or Value Object
- the interface is defined in terms of other elements of the domain model
- the operation is stateless
Life Cycle Management of Domain Objects
Aggregates
- a cluster of associated objects that we treat as a unit for the purpose of data changes
- transaction against the associated objects
- responsibilities
- invariant enforcement
- change management
- consists of a root and a boundary
- root
- a single, specific Entity
- objects outside the Aggregate can reference to only the root
- can be obtained directly from resource (e.g. database)
- other objects must be found by traversal of associations
- boundary
- a delete operation must remove everything within the boundary at once
- when a change to any object within the boundary is commited, all invariants of the whole Aggregate must be satisfied
- root
Factories
- manage the beginning of the object's life cycle
- create and reconstitute complex objects
- delegate invariant checking to objects or aggregate
- ensure that creating objects satisfy client and internal rules
Repositories
- manage the middle and end of the object's life cycle
- motivation
- allowing free access from client to infrastructure such as database leads to complicate the client and obscure model-driven design
- emulate access to infrastructure as if it is just like to manipulate a in-memory collection such as lists and maps
- but leave transaction control to the client
- don't fight frameworks; look for affinities between the concepts of domain-driven design and the concenpts in the framework
- Repositories as object are not necessary when contructing a workflow placing I/O process to the edge in the workflow
Supple Design
Model Integrity Patterns
Smart Constructor
- validate initial values ensuring the created object is valid and holds invariants
Referencies
- A Philosophy of Software Design
- Clean Architecture: A Craftsman’s Guide to Software Structure and Design
- Fundamentals of Software Architecture
- Domain-Driven Design: Tackling Complexity in the Heart of Software
- Domain Modeling Made Functional - Tackle Software Complexity with Domain-Driven Design and F#
- Effective Haskell