Last active
April 12, 2025 21:38
-
-
Save tp-hk/6b81247b7475094f3985e6d0d2ace5c1 to your computer and use it in GitHub Desktop.
Tech Books Summary
- good architecture = allow changes w/ flexibility + delay decision
- Entities: pure; small sets of critical business rules. Should be most independent & reusable. Can be an object with methods or data structures & functions
- Use case: not as pure; is an object. contains data elements & operations.
- Entities are lower level than (don’t depend on) use cases since use cases are closer to inputs/outputs.
- Entities & use cases are 2 layers
- Returned data structure should not reference entities since they might change for diff. reasons
- System should describe what it does not what framework it uses
- Use case layer contains application-layer biz rules. Changes at this layer shouldn’t affect entity layer
- Changes to workflow requires updating use case layer
- Adapter layer: convert data from/to use cases & entities to/from external (web/DB). Example: Views and controllers in MVC; other data from/to internal/external form
- Dependency rule: Dependency should point inward (lower level layers)
- If need to call from inner to outer layer, inner should call an interface implemented by outer layer. Both inner & outer will depend on (point to) interface
- Data crossing boundaries should be simple or just function args (no entities)
- Interface adapters: covert data from format of inner layer to the format of external layer
- Presenter accepts data from application and applies formatting. Presenters are testable
- View (humble object) receive view model from presenters w/o processing. Views display the data
- Humble object patterns separates testable (non-humble) and non-testable (humble) parts
- Objects are not data structure. Objects is a set of operations (pubic methods). DS has no behaviors
- Humble object pattern is likely found in every architectural boundary
...
- MS is not the goal, goal might be e.g. faster delivery, team automany, break off from monolith release cycle. Need ROI calculation, ned to align with what business tries to achieve
- Consider microservice alternatives, or try things in parallel. Alternative ideas:
- Modular monolith
- Vertical scaling (more powerful machine)
- Horizontal scaling (more machines)
- Scale up development team
- New technology
- Use sliders to analyze competing priorities
- Faster delivery: should run path-to-production analysis to find the biggest blocker
- MS: Benefits
- More robust architecture (ability to react to expected variations) because of decomposed functionality
- Great options for scale-up after initial success
- MS: When's MS a bad idea
- Unclear domain/decompose prematurely
- Startup/greenfield project
- Customer-installed software
- Lack of good reason
- Incremental migration:
- Start somewhat small
- The impact of decomposition will be relfected in production env, not during development
- Easier places to experiment - start with whiteboarding
- Where to start migration/decomposiition
- Develop domain model with just enough info (e.g. use event-storming)
- identify bounded context
- BC is good starting points for defining MS boundaries
- map out BC dependencies to determind which is easier to extract (Fig 2.6)
- caveat: domain model represents logical view, not how code is organized
- Use trade-off diagram (benefit of decomposition vs ease of decomposition) to prioritize decomposiition (Fig 2.8)
- Reorganizing teams
- DevOps (independent, autonumous teams)
- Changing/improving developer skills
- How to know if transition is working
- Define measures to track
- Regular checkpoints (review quantitative + qualitative measures)
- Quantitative measures e.g. number of deployments, failure rates, cycle time
- Qualitative measures e.g. team's feelings
- Avoid sunk cost fallacy
- Key: take small steps, be open to new approaches
- Misc.
- "Reuse is not a direct outcome people want. Reuse is something people hope will lead to other benefits"
- Irreversible vs reversible decisions
- Do migration over small steps, allow going back if needed. For each step, copy code instead of changing functionality
- First thing to consider is whether monolith will be modified (more flexibility).
- Biggest barrier is code isn't organized around business domains. Can use seam - a seam is a defined around the code to chnage, then work on new implementation, and swap after change has been made.
- Rewrite - try salvage existing codebase first. If not, rewrite small pieces of functionality at a time and release regularly.
- Migration patterns: Strangler fig application
- New & old systems coexist, allow new code to grow incrementatlly and eventually replace old system. Easy rollback if required.
- Useful when: no need to touch existing system/existing system is black box, works well wehn functionality to move isn't deep inside system. Existing code can still be worked on by others.
- Steps:
- 1: Identify asset to move
- 2: Start implmentating new functionality in microservice. Deploy but not released to public. Parallel run with old code
- 3: Redirect calls to microservice. Prereq: need to have clear inbound calls. If not, consider Branch by Abstraction pattern
- Variation: "Shallow" extraction - Existing functionality will be exposed to MS
- Use HTTP proxy to redirect calls
- Migration patterns: UI composition
- Example: Modules are migrated to MFEs one at a time; For mobile app: configs and layout of UI components is defined in declarative fashion on server side, so UI can be changed without new release
- Migration patterns: Branch by Abstraction
- Useful when: functionality to extract is deep inside existing system i.e. strangler fig is not suitable, or when changes will take a long tim. Using long-lived branches with new functionality getting developed is an option but not optimal. Branch by Abstraction will be a better option.
- Try to use strangler fig pattern before considering this one
- Steps:
-
- Create an abstraction for the functionality to be replaced
-
- Change existing client to use the abstraction
-
- Create new implementation using the abstraction. New impl. can be microservice etc/
-
- Switch abstraction to use new implementation e.g. use feature flags
-
- Clean up old code and optionally abstraction
-
- Fallback: if new implementation fails, switch back to old impl.
-
- Migration pattern: Parallel Run
- Both new + old impl. will be called, but only one (typically old impl.) will be considered source of truth.
- Useful for verifying functional and non-functional parameters of new impl. especially for high risk areas
- End goal isn't to replace the implmentations, but to reduce bugs in one of the impls.
- Spy tools can be used to intercept/stub functionality e.g. verify email should be sent without actually sending
- Parallel run isn't canary release, which redirect some users to new functionality.
- Parallel run is a way to implement dark launching (function released but invisible)
- Parallel run, canary releasing, dark launching all work to support progressive delivery
- Avoid functional decomposition, or domain decomposition
- Avoid bloating clients/thin service
- Decompose based on volatility
- Each areas of change is encapsulated (in a vault) - each represents an area of volatility
- No block if no volatility
- Ask:
- are volatilities in software system?
- any interaction between areas of volatility?
- layers imply top-down. Each layer is also an encapsulation. Within a layer are services (entities)
- layers (top to bottom):
- client layer (user/another system/techs) - volatility in client technology. Try to equalize all clients -- single point of entry to system
- business layer - volatility in changes of system behavior. Good requirement is always behavioral (leave less room for interpretation) not functional i.e. use case/sequence of activities.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment