Blog • Artigo
    ArquiteturaSoluções

    Solution Architecture in Practice: From Business Requirements to Structural Decisions

    Thesis: every architectural decision is a promise made to the business. The work of a solutions architect is not to promise a lot. It is to promise only what can be explained, costed, and verified.

    Alexsander
    AlexsanderEngenheiro de Software
    Oct 26, 2026
    9 min de leitura
    Solution Architecture in Practice: From Business Requirements to Structural Decisions
    1. The craft, without romance

    After more than fifteen years designing and maintaining systems, I reduced the work to a translation with three questions I ask of every structural decision, mine or someone else’s:

    Who pays for this? What business requirement justifies this decision?
    What does it cost me? What complexity, risk, or rigidity am I buying?
    What would make me undo it? What evidence would prove I was wrong?

    The third is what separates maturity from conviction. A decision without a reversal trigger is not a decision: it’s a dogma with a diagram. And a decision without an explicit reason behind it—whether a business requirement, legal constraint, or risk mitigation—is just technical preference with a budget. The trigger does not always mean undoing: it can indicate review, containment, or progressive replacement, because not every decision can be reversed at an acceptable cost.

    In this text I apply these questions to a real product I architected, which uses AI, is in production, and continues evolving. The method works for any business model. The decisions do not. That is the difference between a method and a recipe.

    Regarding the case state: I describe recorded decisions and the target architecture. The production product coexists with legacy during the transition. I do not publish measured results I don’t have, because inventing a metric would be the opposite of this article’s thesis.

    1. The case and its premises

    A software‑as‑a‑service platform that organizes the operation of small service providers and uses AI as a differentiator. It started as a proof of concept, became an MVP, and today serves real customers, with subscriptions in three plans, a free trial on the entry plan, and usage‑based pricing.

    The premises that shape everything:

    • small team, no slack to operate complexity;
    • product in production: mistakes cost and there are customers depending on it;
    • third‑party personal data under LGPD;
    • many low‑ticket customers, making the cost to serve each a matter of survival;
    • AI as a differentiator, not as an end.
    1. From business requirements to drivers
    Business factDriverWhat it demands from the design
    Personal data and commercial information of customersTrust and isolationIsolation is a core requirement, not a detail
    Many low‑value customersLow marginal costAvoid operating a new unit per customer
    Free trial and usage‑based pricingImmediate onboarding and consistent measurementAutomated provisioning and consumption metering
    Small teamEvolvabilityOperational simplicity over sophistication
    AI as a differentiatorSubstitutability and controlAI does not dictate the core shape
    Product in productionContinuityControlled evolution, with possible rollback

    Continuity, here, has an operational definition: preserve customers’ critical operations and prevent data loss or inconsistency in any change. A brief outage outside critical hours is tolerable. Corrupting data is not.

    1. The five decisions: promise, cost, and reversal trigger

    Each decision below follows the same format, on purpose. It forces you to state what is normally implicit.

    Decision 1: isolation between customers in two layers

    The promise. A customer’s data never appears to another. Multi‑tenancy is not a single choice; it is dimensions of sharing (application, database, schema, resources) that combine and condition each other. The decision was to share application, database, and schema, with logical isolation imposed in two layers: the application and the database itself.

    ModelAdvantageTrade‑off
    Shared database and schemaLower cost, simple provisioningRelies more on logical isolation
    Separate schemas in the same databaseMore explicit separationMultiplying migrations
    Database per customerStrong operational isolationHigher management and cost
    Dedicated instanceMaximum autonomyHigher cost per customer

    Why this one. Isolated cost alone does not decide: a database per customer, with good automation, could also be acceptable. The combination of marginal cost, immediate provisioning for the free trial, and a small team—where each extra unit is another item to migrate, monitor, and restore—tips the balance.

    The cost. In a shared model, isolation ceases to be a by‑product of design and becomes a property that must be proven continuously. The second layer exists because the first will fail someday. The goal is that an authorization failure in the application does not become a data‑exposure incident, while the database enforces its policies with trusted credentials and contexts. It is a goal, not a guarantee: common‑cause failures, such as over‑privileged credentials, bring down both layers together.

    The mechanism. The second layer relies on row‑level security policies in the relational database, on connection roles without privileges to bypass them, on reliable propagation of the client context to the session, and on negative access tests. Today, production isolation is enforced at the application layer. Enforcement in the database belongs to the target architecture and becomes a fulfilled promise only when negative tests demonstrate it.

    What would make me undo it. A client whose contract requires physical separation; isolation tests that cannot be maintained at reasonable cost; or the cost of proving isolation surpassing the cost of actually separating.

    Decision 2: a modular monolith, not microservices

    The promise. Evolve quickly with few people, without paying the distribution tax.

    Why this one. Microservices can offer deployment autonomy, independent scaling, and failure isolation, at the cost of distributed complexity. Many of those benefits are also attainable in a modular monolith, with separate work processes, resource isolation, and component‑level scaling, although the degree of independence is not equivalent. At this stage, the additional operational, observability, and coordination costs did not justify the expected benefits.

    The defined target architecture concentrates the backend in a single modular codebase, with explicit boundaries between business capabilities and the ability to run operational components in separate processes when needed, such as asynchronous AI tasks. Within each module, the style is hexagonal. The two ideas solve different problems: the modular monolith splits the business; the hexagonal style orders the dependencies.

    The cost. Boundaries that are not enforced do not exist. They require automatic verification of dependencies and public interfaces between modules, so that no one can reach another’s internals. Without that, you have a monolith with pretty folders.

    What would make me undo it. Measured asymmetric scaling, not assumed; independent teams blocked by joint deployments; or boundaries that degrade despite verification.

    Components and responsibilities, not rigid layers:

    ComponentResponsibility
    User interfacesThree web front‑ends over the same API contract
    API contractFormal integration boundary
    Business modulesProduct capabilities, each with a public interface
    Domain and applicationRules, invariants, authorization, and orchestration within each module
    Ports and adaptersPersistence, AI, and integrations, replaceable
    Data and platformDatabase with per‑customer isolation, work processes, observability

    Decision 3: AI as replaceable infrastructure

    The promise. AI delivers value without dictating the rest of the system.

    Why this one. I separate the deterministic from the probabilistic. The business core—rules, billing, permissions—must be predictable. AI analyzes, recommends, and assists. With contracts and a deterministic simulator by default in development, I can swap providers without rewriting the domain, test at no cost, and prevent agent technology from contaminating the rules. None of this requires AI to run in the same process as the application. But technically replacing a provider is not the same as porting behavior: different models vary in quality, latency, cost, response format, and tooling, and the swap is only complete after a quality and security assessment.

    The cost. Two cautions. Not everything involving AI is infrastructure: deciding when to request an evaluation, validating the result, and requiring human review are application policies. And when an output can affect people, the domain preserves the rules and invariants, AI provides assistive assessments, and the final decision remains under human authorization, validation, and supervision.

    There is also a direct link to Decision 1. Every AI execution must respect its authorization context, whether user, service, or automated process. Administrative tasks may have a different scope from a user’s, provided it is explicit, minimal, and auditable. An execution that exceeds its scope could compromise the isolation guarantees established by the rest of the system.

    What would make me undo it. A provider capability that the contract cannot express without distorting the domain; or the cost of maintaining the replacement layer exceeding the risk it mitigates.

    Decision 4: the API contract as an artifact

    The promise. Backend and interfaces evolve without silently breaking each other.

    Why this one. The contract is formal and the client consumed by the front‑ends is generated from it. This reduces divergences and catches part of the incompatibilities already during development.

    The cost. It does not catch everything. Behavioural changes, semantic differences, and drift between contract and actual implementation still reach production. Therefore the generated client needs contract and compatibility tests, and the versioning policy must consider that independent interfaces do not evolve together.

    What would make me undo it. Recurring incompatibilities that the process does not capture, a sign that the contract describes intent rather than behaviour.

    Decision 5: evolve the system without stopping the product

    It is the most expensive decision and the easiest to take for the wrong reasons. There are four paths, often mixed:

    PathMeansBenefitRisk
    Fix within the current architectureAdjust what existsSmallest changePreserve structural limitations
    Refactor internallyReorganize current codeLower migration riskResidual debt
    Replace piece‑by‑pieceNew components take over capabilities, one by oneControlled modernizationIntegration during transition
    RewriteNew system replaces the old oneRedesigned boundariesRegression, cost, and schedule

    The recorded architectural proposal was to rewrite the backend as a hexagonal modular monolith in Python, keeping TypeScript for the interfaces. Its execution remains conditioned on validating structural limitations, functional equivalence, and migration feasibility. Previously, the proposal was to keep the core in TypeScript and create AI as a separate service. That was revised. I treat the revision as part of the method: a decision is a hypothesis with a review trigger, and changing it based on evidence is maturity.

    This decision is intentionally the one that demands the most scepticism, including my own. Hence I use a criterion before rewriting anything:

    The rewrite test. I rewrite only if all four answers are “yes”:

    1. The limitation is structural, i.e., it does not
    Este conteúdo foi útil?
    Compartilhar artigo

    Quer aplicar isso no seu contexto?

    Vamos conversar sobre seus desafios e encontrar o melhor caminho para sua operação.

    Agendar conversa