Additional Design
Considerations
Programming paradigms, generative approaches, annotations, AOP, and cross-cutting concerns in software architecture.
Programming Language Features & Paradigms to Reduce Complexity
Design patterns and principles can be realized through real tools, technologies, and programming language features. Generative approaches reduce boilerplate, while language paradigms enforce correctness at the type-system level β both directly reduce complexity in software architecture.
Instead of writing repetitive code by hand, generative approaches let you describe what you want at a higher level β and the tooling produces the actual code for you.
- β Use macros, templates, schemas, or transpilers as your source
- β Eliminate manual boilerplate β less code to write and maintain
- β Get consistent output across multiple languages and platforms
- β Keep a single source of truth that enforces DRY automatically
Macros
- C/C++ Preprocessor β named fragments replaced at compile time
- Enforce DRY by expanding repetitive code patterns automatically
- Result is high-performance runtime code
Template-Based Generators
- JET β Java Emitter Templates
- JSP / JSF β Jakarta Server Pages / Faces
- Thymeleaf, FreeMarker β server-side Java templating
- Follows DRY + SoC; output is static & high performance
Transpilers
- TypeScript β JavaScript (adds type safety)
- Wt β C++ Web Toolkit
- JWt β Java Web Toolkit
- Cross-platform: Xamarin, React Native, Flutter, Phone Gap
C/C++ macros let you define a reusable fragment once and expand it everywhere at compile time. The preprocessor replaces every occurrence before the compiler even sees the code β zero runtime overhead, pure DRY.
β Without Macros β Repeated Code
β With Macros β Define Once, Reuse
Template engines mix static HTML with dynamic placeholders. At build or request time the engine fills in the data and outputs pure HTML β the template is the single source of truth for layout, while the data comes from the backend.
Thymeleaf Template (source)
Generated HTML (output)
TypeScript adds a compile-time type system on top of JavaScript. The transpiler strips types and outputs standard JS β catching entire classes of bugs before runtime at zero cost to performance.
TypeScript β Source (typed)
JavaScript β Generated (runtime-ready)
gRPC uses Protocol Buffers to define a .proto schema once, then generates type-safe client/server stubs for Java, Python, C#, Go, and more β enforcing contracts across service boundaries.
π product.proto β The Schema (source of truth)
βοΈ How it works
- Define messages (data structures) and services (RPC methods) in one
.protofile - Run
protocβ the Protocol Buffer compiler β targeting any language - The compiler outputs type-safe stubs: data classes + client/server interfaces
- All languages share the same contract β no drift, no manual mapping
.proto and regenerate β every language stays in sync automatically.Generate Java Code
Generate Python Code
Generate C# Code
Programming paradigms provide structural building blocks that manage complexity at different levels β from naming and grouping code, to enforcing type safety and memory ownership at compile time.
Functions & Procedures
- SRP, Modularity, SoC, DRY β core principles realized
- Decompose behaviour into named, reusable units
- Enable isolated testing and reasoning
OOP β Objects & Inheritance
- Encapsulation, polymorphism, abstraction
- Class hierarchies and interfaces
- Modelling real-world domains in code
Modules & Packages
- Logical grouping of related code
- Namespace isolation prevents naming conflicts
- Enables independent compilation & deployment
Access Modifiers
- public β accessible anywhere
- private β class-only
- protected β subclasses
- internal β same assembly / package
Modern languages go beyond basic paradigms with features that catch entire categories of errors at compile time β type systems, ownership models, async primitives, and optional types.
Type Systems
- Catch type errors at compile time before they reach production
- C++:
int,double,bool,string - Prevents misuse:
string phone = 053β¦β compile error - Stronger types = fewer runtime surprises
Ownership Models
- Memory safety: each object has a clear owner
- Prevents memory leaks, dangling pointers, data races
- Helps with concurrency: no shared mutable state
unique_ptr(single owner) vsshared_ptr(reference counted)
Promises & Futures
- Async operations without blocking the main thread
- JS:
Promise.then(success, failure) - Chain multiple async results declaratively
- Eliminates "callback hell" and race conditions
Optionals
- "Expect Errors" principle β Postel's Law in code
- Java:
Optional<User> - Check presence before access β avoids NPEs
- Makes absence of a value explicit in the type system
- Generative creation β Macros, templates, transpilers, and API generators (gRPC) produce code from higher-level descriptions, enforcing DRY and reducing boilerplate
- Transpilers β TypeScript β JS adds type safety at zero runtime cost; cross-platform frameworks (Flutter, React Native) target multiple devices from one codebase
- API-based generators β Define a
.protoschema once β generate type-safe stubs for every language - Paradigms β Functions (SRP), OOP (encapsulation), Modules (namespace isolation), and Access Modifiers (information hiding) directly reduce complexity
- Advanced features β Type systems, ownership models, promises/futures, and optionals catch entire categories of errors at compile time
Annotations and Aspect-Oriented Programming
Annotations (decorators) add metadata to code elements, evaluated at compile or runtime. AOP separates cross-cutting concerns from business logic, weaving them in transparently β applying DRY, SoC, and OCP to concerns like logging, security, and performance monitoring.
What are Annotations?
- Decorate functions, classes, methods, or properties with metadata
- Evaluated by an annotation processor at compile time or runtime
- Provide an abstraction layer to manage complexity
- Enable cleaner, more concise, and more maintainable code
- Help build more reliable and scalable systems
Compile-Time β Lombok Example
Runtime annotations are evaluated by frameworks at startup or during execution. They wire dependencies, map objects to database tables, expose REST endpoints, and more β all without manual boilerplate.
Spring DI β @Bean Configuration
Hibernate ORM β @Entity Mapping
AOP increases modularity by separating cross-cutting concerns (logging, security, transaction management) from business logic. The concern logic is defined once in an Aspect and "woven" into business code at compile or runtime.
What is AOP?
- Increases modularity by separating cross-cutting concerns
- Weaves concern logic into business logic at compile or runtime
- Applies DRY + SoC + OCP to concerns like logging & security
Key Concepts
- Aspect β component encapsulating cross-cutting logic
- Advice β the action to perform (before, after, around)
- Join Point β point in execution where advice is applied
- Pointcut β expression to select matching join points
Benefits
- Business logic free of cross-cutting noise
- Single place to change concern behaviour
- Cleaner, more readable service classes
- More reliable and maintainable systems
β Before AOP β Logging Mixed In
β After AOP β Aspect Handles Logging
@LogExecutionTime β no logging logic in business code. The aspect is "woven in" by the Spring AOP framework at runtime, keeping business logic clean and cross-cutting concerns centralized.- Annotations β Metadata decorators evaluated at compile time (Lombok: @Getter, @Setter) or runtime (Spring: @Bean, @Configuration; Hibernate: @Entity, @Column)
- Compile-time annotations generate code automatically (getters, setters, constructors) β eliminating boilerplate
- Runtime annotations wire dependencies, map ORM entities, expose REST endpoints β all without manual configuration
- AOP β Separates cross-cutting concerns (logging, security, transactions) from business logic via Aspects
- AOP Key Concepts: Aspect (the concern module), Advice (the action), Join Point (where it applies), Pointcut (the selector expression)
- Benefits: DRY + SoC + OCP β define each concern once, change it in one place, business code stays clean
Cross-Cutting Concerns and Concepts
Every system has concerns that don't belong to a single component β they span the entire architecture. These are called cross-cutting concerns.
What are they?
- Aspects of the system that affect multiple components simultaneously
- Cannot be cleanly isolated into a single independent module
- Arise naturally from quality requirements (security, performance, reliability)
The Problem
- If hard-coded everywhere β violates DRY & SoC
- Logic gets scattered across many components
- A single change (e.g. log format) requires touching every class
- Testing becomes difficult β concerns are tangled with business logic
The Solution
- Identify and centralise each concern in one place
- Use AOP, frameworks, or dedicated infrastructure modules
- Apply consistently β every component benefits automatically
- Change once β propagates everywhere
"Cross-cutting concerns cannot be easily separated into independent components β they may violate DRY/SoC and can lead to code scattered across different parts of the system."
Persist data from volatile memory to permanent storage. The goal is to decouple business domain from database infrastructure and provide a single persistence solution for the entire application.
What & Why
- Persist data from volatile memory to permanent storage
- Decouple business domain from database infrastructure
- Abstract database interactions into high-level object operations
- Provide a single persistence solution for the entire application
ORM Framework Examples
- Hibernate β Java
- Entity Framework β .NET
- Sequelize β Node.js
- SQLAlchemy β Python
A uniform, system-wide strategy for handling errors ensures predictable responses depending on category and severity, minimizes the blast radius of failures, and facilitates diagnosis.
Goals of Error Handling
- Predictable responses depending on category & severity
- Differentiate between logical and technical errors
- Minimize effects of errors on the system
- Uniform & consistent across different technologies
- Facilitate diagnosis β what / where / why
- Error prevention β timely warning of identifiable problems
HTTP Status Codes
| Code | Meaning |
|---|---|
| 400 | Bad Request |
| 401 | Unauthorized |
| 404 | Not Found |
| 405 | Method Not Allowed |
| 500 | Internal Server Error |
| 503 | Service Unavailable |
| 507 | Insufficient Storage |
Security is a first-class architectural concern β it should be designed in from the start, not retrofitted. It spans authentication, authorization, data integrity, confidentiality, and system availability.
Authentication & Authorization
- Authentication β determine the sender's identity
- Authorization β grant rights based on verified identity
- Example: JWT tokens, OAuth 2.0, SAML
Integrity & Confidentiality
- Integrity β detect manipulation of data
- Confidentiality β restrict unauthorized data access
- Non-repudiation β prove authorship & validity
Availability & Implementation
- Availability β precautions against system failure
- Frameworks: Spring Security (Java), Django (Python)
- Libraries: OpenSSL, Bouncy Castle
- Security should not be retrofitted β design in from the start
Internationalization prepares an application to support multiple locales without code changes β covering formatting, currency, date/time, labels, cultural differences, and more.
I18N Dimensions
Implementation Tools
- Resource Bundles β locale-specific key/value property files (Java)
- i18next β widely used JS internationalization framework
- Globalize β Unicode CLDR-based formatting
- jQuery.I18n β plugin for web UIs
Components and services must communicate β the communication style (sync vs. async, RPC vs. pub/sub) and technology choice are cross-cutting decisions that affect the entire system.
Communication Styles
- Synchronous β caller blocks until response received
- Asynchronous β fire-and-forget; callback/event-based
- Remote Procedure Call β gRPC, Apache Thrift, SOAP
- Publish/Subscribe β decoupled topic-based messaging
- Broadcast β one sender, many receivers
Technologies
- Message Queues/Brokers: RabbitMQ, Kafka, Amazon SQS
- RPC Standards: gRPC, Apache Thrift, SOAP
- API Styles: REST, GraphQL
- Networking Protocols: HTTP, RTP, UDP Multicast, WebSockets
The UI is where users interact with the system. A cross-cutting UI strategy ensures consistent styles, responsive layouts, accessibility (WCAG), and reusable components across all platforms.
User Interface
- Graphical, textual, voice-control interfaces
- Can be adaptive and/or adaptable (device, user, situation)
- Platforms: Mobile (iOS/Android), Desktop, Web, Touch monitors
- Responsive UIs β pages rearrange based on screen size
- Consistent stylesheets, reusable UI elements
- WCAG β Web Content Accessibility Guidelines
Ergonomics
- Optimize usability and user experience
- Interaction flow and design
- Reactivity (perceived performance)
- Availability, protection against user errors
- Learnability, self-descriptiveness
- Adaptive design β different layouts per platform
Domain-specific business rules like "if process X is finished, then do Y" are often distributed across many components. This makes them hard to find, hard to change, and expensive to maintain. A centralized approach is better.
The Problem with Hard-Coded Rules
- Domain conditions scattered across many components β low maintainability
- Complex domain knowledge embedded in code β confusing code
- Simple changes can become expensive β hard to estimate effort
- Testing is difficult β business logic intertwined with infrastructure
Drools β Business Rules Engine
Definition
- Aspects of the architecture affecting multiple components or the entire system
- Usually cannot be easily separated into independent components
- Often arise from fulfilling quality requirements
Challenge
- Impact the entire system, adding technical constraints
- May violate DRY/SoC if not managed
- Can scatter code across different components
- Hard-coded rules reduce maintainability
Solution Approaches
- AOP β weave cross-cutting logic at compile/runtime
- Frameworks β Spring Security, Hibernate, i18next
- BRMS β Drools for centralized business rules
- Workflow Engines β Camunda, Uber Cadence
- Cross-cutting concerns β Architecture aspects that affect multiple components: persistence, error handling, security, I18N, communication, UI/UX, business rules
- Persistence β Decouple domain from DB via ORM frameworks (Hibernate, Entity Framework, SQLAlchemy)
- Error Handling β Uniform, predictable responses; differentiate logical vs. technical errors; facilitate diagnosis
- Security β Authentication, authorization, integrity, confidentiality β design in from the start, never retrofit
- I18N β Prepare for multiple locales (formatting, currency, date/time, labels) via resource bundles and I18N frameworks
- Communication β Choose sync/async, RPC/pub-sub, REST/GraphQL β the style affects the entire system
- UI & Ergonomics β Consistent styles, responsive layouts, WCAG accessibility, reusable components
- Business Rules β Centralize with BRMS (Drools) or workflow engines (Camunda, Cadence) instead of scattering across code
- Solution Approaches β AOP, dedicated frameworks, BRMS, and workflow engines keep cross-cutting concerns manageable
C/C++ macros are fragments of code that have been given a name. Whenever the name is used, it is replaced by the contents of the macro.
gcc.gnu.org/onlinedocs/cpp/Macros.htmlJET β Java Emitter Templates for generating Java source code from templates.
eclipse.org β JET TutorialJakarta Server Pages (formerly JavaServer Pages) β server-side templating for Java web applications.
wikipedia.org β Jakarta Server PagesA modern server-side Java template engine for both web and standalone environments.
thymeleaf.orgA Java library to generate text output (HTML, e-mails, config files, source code) based on templates and changing data.
freemarker.apache.orgA web GUI library written in modern C++ for building interactive web applications.
webtoolkit.eu/wtA web GUI library in pure Java β write UI code once, run in the browser.
webtoolkit.eu/jwtTypeScript is JavaScript with syntax for types β adds compile-time type safety to JS.
typescriptlang.orgAn open-source UI framework for building Android, iOS, macOS, Windows, and Web apps from one codebase.
reactnative.devAn open-source UI SDK to develop cross-platform apps (Web, Android, iOS, Linux, macOS, Windows) from a single Dart codebase.
flutter.devA cross-platform, open-source, high-performance remote procedure call (RPC) framework originally developed at Google.
grpc.ioA data query and manipulation language for APIs β lets a client specify exactly the data it needs. A server can fetch from multiple sources and return a unified result.
graphql.orgReduce Complexity via Language
- Leverage generics, annotations, lambdas & type systems
- Macros & code generation eliminate boilerplate
- Paradigms (OOP, FP) shape how you decompose problems
Aspect-Oriented Programming
- Annotations add declarative metadata to code
- AOP weaves cross-cutting logic (logging, security) at compile/runtime
- Keeps business logic clean & focused
Architecture-Wide Aspects
- Persistence, error handling, security, I18N, communication
- Design security in from the start β never retrofit
- Centralize rules via BRMS / workflow engines
| id | first_name | |
|---|---|---|
| 1 | Alice | alice@example.com |
| 2 | Bob | bob@example.com |
| 3 | Carol | carol@example.com |
| Key | π¬π§ EN | π©πͺ DE | π«π· FR |
|---|---|---|---|
| app.title | Product Store | Produktshop | Boutique Produits |
| welcome | Welcome, Alice! | Willkommen, Alice! | Bienvenue, Alice! |
| btn.buy | Add to Cart | In den Warenkorb | Ajouter au panier |
| error.notfound | Item not found | Artikel nicht gefunden | Article introuvable |
.properties files differ. Adding a new language means adding one file, nothing else.