DEV Community

Cover image for The A2A Protocol Misconception: Why Your Agent Architecture Matters More Than Your Framework
Seenivasa Ramadurai
Seenivasa Ramadurai

Posted on

The A2A Protocol Misconception: Why Your Agent Architecture Matters More Than Your Framework

Building A2A Agents That Last: Where Architecture Actually Matters

When people talk about AI agents today, most conversations jump straight into frameworks, SDKs, or cloud services. LangChain, LangGraph , Google ADK, Bedrock, Microsoft Agent frameworks, Tool calling, Memory, Planning.

But that's not where things break in enterprise systems.

They break much earlier at the architecture level.

I ran into this myself while working through A2A (Agent-to-Agent) implementations and trying to design agents that are not demos, not experiments, but systems that are supposed to live inside enterprises for years. The more teams I worked with, the clearer one pattern became: teams were treating A2A as an "agent framework" when it's actually a communication protocol. And confusing the two leads to fragile designs.

This is where Hexagonal Architecture, AgentExecutor, and the idea of an Agent Core must be understood correctly.

The Problem: Mixed Concerns

The most common failure mode in A2A agent implementations is mixing protocol concerns with business logic. When teams implement their decision making, business rules, and domain intelligence directly inside the AgentExecutor the component that handles A2A protocol messages they create systems with four critical problems:

Problem 1: Untestable Logic

Business rules can't be tested without spinning up full A2A infrastructure. Simple policy changes require constructing protocol objects, managing task states, and handling streaming semantics just to verify a decision is correct.

Problem 2: Unreusable Intelligence

The agent's core logic becomes hardwired to A2A objects. Using the same decision making in a batch job, scheduled workflow, or different protocol requires extracting and rewriting logic that's tangled with protocol handling code.

Problem 3: Unreadable Business Logic

Understanding what the agent actually does requires first learning the A2A specification, SDK objects, protocol patterns, and streaming semantics. The business logic that matters is buried under protocol complexity.

Problem 4: Unevolvable Systems

Changing frameworks, switching protocols, or adding capabilities means touching protocol handling and business logic simultaneously. Every evolution requires coordinated changes across architectural layers that should be independent.

The architecture pattern described in this post solves these problems through strict separation of concerns: A2A handles communication, AgentExecutor handles translation, and Agent Core handles intelligence. Each layer can evolve independently.

Understanding A2A: Communication, Not Intelligence

Let's be precise about what A2A actually is, because this is where the confusion starts.

When Google and 50+ partners launched the Agent2Agent (A2A) protocol in April 2024, they were solving a specific problem: how do agents built with different frameworks communicate with each other?

According to the official A2A specification, A2A is "an open standard designed to facilitate communication and interoperability between independent, potentially opaque AI agent systems."

Read that carefully: communication and interoperability. Not intelligence. Not reasoning. Not decision-making.

A2A's Actual Scope

A2A standardizes how agents:

  • Discover each other's capabilities through Agent Cards (JSON metadata documents)
  • Negotiate interaction modalities (text, files, structured data)
  • Manage collaborative tasks through a defined task lifecycle
  • Securely exchange information without needing access to each other's internal state, memory, or tools

The core data model is straightforward:

Task - The fundamental unit of work, with states: submitted, working, input-required, completed, failed, canceled

Message - A communication turn between client and agent, containing one or more Parts

Artifact - Tangible outputs generated by the agent (documents, images, deliverables)

Part - The atomic unit of content (text, files, JSON, forms)

Agent Card - JSON document describing an agent's capabilities, endpoints, and authentication requirements

The protocol operations are similarly focused: sending messages, streaming responses, getting task status, listing tasks, canceling tasks. These are protocol-level concerns.

A2A typically uses HTTP/HTTPS with JSON-RPC 2.0 for payload formatting. It supports request/response, streaming via Server Sent Events, and push notifications for long running tasks.

What A2A Intentionally Doesn't Define

The A2A specification explicitly does not tell you:

  • What your business rules should be
  • How to make decisions
  • How to plan or reason
  • How to architect your memory
  • How to use LLMs

The spec states that agents collaborate "without needing access to each other's internal state, memory, or tools." This is by design. A2A is opaque by intention.

A2A is a transport layer. It's how agents talk to each other. That's its job. That's all it does.

Where Teams Actually Go Wrong: The AgentExecutor Trap

Here's where the architectural mistake typically happens.

The A2A Python tutorial introduces a component called AgentExecutor. It's presented as the pattern for implementing the agent-side logic that responds to A2A protocol requests.

Because AgentExecutor is the entry point for all A2A messages receiving Messages, accessing Task objects, handling streaming there's a natural gravitational pull to put your logic there. After all, it has access to everything: the incoming message, the task context, the ability to create artifacts and send responses.

This is the trap.

What AgentExecutor Actually Handles

Look at what AgentExecutor deals with from the A2A protocol perspective:

  • Receiving SendMessageRequest objects with Message payloads
  • Managing Task state transitions (submitted → working → completed)
  • Emitting TaskStatus updates and TaskArtifactUpdate events
  • Handling streaming via Server-Sent Events
  • Processing cancellation requests
  • Managing contextId for multi-turn conversations

These are protocol-level responsibilities. They're about the mechanics of A2A communication.

What Your Agent Actually Needs to Do

Now contrast that with what your agent's intelligence needs to handle:

  • Evaluate an expense approval request against company policy
  • Determine which candidate profiles match hiring criteria
  • Decide whether a customer inquiry needs human escalation
  • Plan a multi-step research workflow
  • Apply business rules to structured form data

These are domain level responsibilities. They're about your business logic, your policies, your decision making criteria.

When these two layers get mixed when business rules live inside AgentExecutor alongside protocol handling—you create a system with four critical failure modes.

Failure #1: Can't Test Without Infrastructure

Want to verify that your expense approval rules work correctly? You now need to spin up an A2A server, construct proper SendMessageRequest objects, manage Task state, and handle event queues.

Just to test a business rule.

Failure #2: Can't Reuse Logic

Maybe you need the same approval logic in a batch processing job, or a scheduled workflow, or a different protocol.

Too bad. It's hardwired to A2A objects.

Failure #3: Can't Understand Without Protocol Knowledge

New team members need to learn the A2A specification, SDK objects, and protocol patterns before they can comprehend your business logic.

The logic that actually matters to the business is buried under protocol complexity.

Failure #4: Can't Evolve

Want to switch from LangChain to LangGraph? Move to a different communication protocol? Add a new capability?

Every change touches protocol handling AND business logic simultaneously. You're rewriting everything.

The Solution: 3-Layer Architecture

Here's the architecture that prevents collapse:

The Non-Negotiable Rule

Nothing from the Protocol Layer crosses into the Agent Core Layer.

Period.

If your agent core imports A2A SDK types, your architecture is broken. This isn't negotiable. This is structural necessity.

Before & After: The Code Difference

The Wrong Way: Everything Tangled Together

Here's what most teams write when they first implement an A2A agent:

# agent_executor.py - EVERYTHING mixed together

from a2a import SendMessageRequest, Task, Message, Artifact

class ExpenseAgentExecutor:
    async def execute(self, request: SendMessageRequest) -> Task:
        # Protocol handling mixed with business logic
        message = request.message
        task = Task(state="working")

        # Business logic coupled to A2A objects
        expense_data = self._extract_from_message(message)
        amount = expense_data.get("amount")

        # Decision making in protocol adapter!
        if amount > 5000:
            approval = "CFO_REQUIRED"
        else:
            approval = "APPROVED"

        # Protocol handling again
        artifact = Artifact(content={"status": approval})
        task.artifacts.append(artifact)
        task.state = "completed"

        return task
Enter fullscreen mode Exit fullscreen mode

Why this is problematic:

This code violates separation of concerns in multiple ways. The business rule (amount > 5000) is buried inside the same method that handles A2A protocol objects (SendMessageRequest, Task, Artifact). This means:

  • Can't test the approval logic without A2A: To verify the > 5000 rule works, you need to construct a SendMessageRequest, create Message objects, and handle Task states. Testing a simple business rule requires the entire protocol machinery.

  • Can't reuse this logic elsewhere: If you need the same approval rules in a batch job that processes monthly expenses, or in a different API endpoint, you can't extract this logic without bringing the A2A dependencies with it.

  • Can't change the threshold without risk: Changing 5000 to 3000 means editing code that also manages protocol states. A business policy change touches protocol handling code.

  • Can't understand the business logic easily: Someone reading this code must understand A2A protocol objects before they can figure out what business rule is being applied.

The Right Way: Layered Architecture

Now let's see the same functionality implemented with proper architectural separation.

Step 1: Define Pure Domain Models

First, create domain entities that represent your business concepts with zero protocol dependencies:

# domain/models.py - Pure domain entities

from dataclasses import dataclass
from typing import Optional

@dataclass
class ExpenseRequest:
    """
    Domain entity representing an expense approval request.

    This knows NOTHING about A2A protocol. It's a pure business concept.
    Could be used in A2A, REST, gRPC, batch jobs, anywhere.
    """
    amount: float
    category: str
    requestor: str
    justification: str

@dataclass
class ApprovalDecision:
    """
    Domain result representing the outcome of approval logic.

    Again, zero A2A knowledge. This is the output of your business logic,
    expressed in business terms: approved, who approved it, why.
    """
    approved: bool
    approver: Optional[str]  # "CFO", "MANAGER", "AUTO", or None
    reason: str
    requires_escalation: bool
Enter fullscreen mode Exit fullscreen mode

Why this matters:

These are pure data classes representing business concepts. They have no imports from the A2A SDK. They don't know about Tasks, Messages, or Artifacts. They represent what your domain actually cares about: expense requests and approval decisions.

Because they're pure, you can use them anywhere: in tests, in batch jobs, in different protocols, in UI code. They're portable.

Step 2: Implement Pure Business Logic

Next, implement your agent's actual intelligence with no infrastructure dependencies:

# domain/expense_service.py - Your agent's intelligence

class ExpenseApprovalService:
    """
    Agent Core - contains all business logic and decision-making.

    Notice: NO A2A imports anywhere in this file.
    This is your agent's "brain" - it makes decisions based on business rules,
    not based on what protocol is calling it.
    """

    def __init__(self, policies: dict):
        """
        Configuration is injected, making this testable and flexible.
        You can change thresholds without touching code.
        """
        self.cfo_threshold = policies["cfo_threshold"]
        self.manager_threshold = policies["manager_threshold"]

    def evaluate(self, request: ExpenseRequest) -> ApprovalDecision:
        """
        Pure business logic - testable without any infrastructure.

        This method:
        - Takes a domain object (ExpenseRequest)
        - Returns a domain object (ApprovalDecision)
        - Has ZERO knowledge of A2A, protocols, or communication
        - Can be tested in milliseconds without spinning up servers
        - Can be reused in any context that needs approval logic
        """

        # Business rule 1: CFO approval required for large expenses
        if request.amount > self.cfo_threshold:
            return ApprovalDecision(
                approved=False,
                approver="CFO",
                reason=f"Amount ${request.amount} exceeds CFO threshold",
                requires_escalation=True
            )

        # Business rule 2: Manager approval for medium expenses
        elif request.amount > self.manager_threshold:
            return ApprovalDecision(
                approved=False,
                approver="MANAGER",
                reason=f"Amount ${request.amount} requires manager approval",
                requires_escalation=True
            )

        # Business rule 3: Auto-approve small expenses
        else:
            return ApprovalDecision(
                approved=True,
                approver="AUTO",
                reason="Within auto-approval limits",
                requires_escalation=False
            )
Enter fullscreen mode Exit fullscreen mode

Why this is powerful:

This is your agent's actual intelligence, completely isolated from any protocol. Notice what this enables:

  • Change business rules in one place: Want to add a category-based rule? Add it here. Want to change thresholds? Change the policy config. No protocol code touched.

  • Test in milliseconds: You can write dozens of test cases and run them instantly because there's no infrastructure to set up. Just call service.evaluate() with different inputs.

  • Reuse everywhere: This exact same code works in A2A agents, REST APIs, batch jobs, cron jobs, different protocols. It's protocol-agnostic.

  • Business analyst can read it: The logic is expressed in business terms. Anyone can understand the approval rules without knowing what A2A is.

Step 3: Create a Thin Protocol Adapter

Finally, build the AgentExecutor as a pure translator with no business logic:

# adapters/a2a_executor.py - Protocol adapter ONLY

from a2a import SendMessageRequest, Task, Message, Artifact
from domain.models import ExpenseRequest, ApprovalDecision
from domain.expense_service import ExpenseApprovalService

class ExpenseAgentExecutor:
    """
    Protocol adapter - translation ONLY, no business logic.

    This class has ONE job: translate between A2A protocol objects
    and domain objects. It contains zero business rules, zero decision logic.
    It's a dumb translator.
    """

    def __init__(self, service: ExpenseApprovalService):
        """
        The actual intelligence is injected as a dependency.
        AgentExecutor doesn't contain logic - it delegates to the domain service.
        """
        self.service = service

    async def execute(self, request: SendMessageRequest) -> Task:
        """
        The translation flow in three clear steps:
        1. A2A protocol → Domain objects
        2. Call domain logic
        3. Domain objects → A2A protocol
        """

        # Step 1: Translate A2A protocol → Domain
        # Extract business data from A2A Message and convert to domain entity
        domain_request = self._to_domain(request.message)

        # Step 2: Call domain logic (knows nothing about A2A)
        # This is where the actual intelligence lives
        decision = self.service.evaluate(domain_request)

        # Step 3: Translate Domain → A2A protocol
        # Convert domain result back into A2A Task with Artifacts
        return self._to_a2a_task(decision)

    def _to_domain(self, message: Message) -> ExpenseRequest:
        """
        Translation method: A2A Message → Domain ExpenseRequest

        This method understands A2A protocol structure (Message, Parts)
        and extracts the business data we need. It's pure translation -
        no business logic, no decisions.
        """
        data = self._parse_message_parts(message)

        # Create domain object from protocol data
        return ExpenseRequest(
            amount=data["amount"],
            category=data["category"],
            requestor=data["requestor"],
            justification=data["justification"]
        )

    def _to_a2a_task(self, decision: ApprovalDecision) -> Task:
        """
        Translation method: Domain ApprovalDecision → A2A Task

        Takes the domain result and wraps it in A2A protocol objects
        (Task, Artifact). Again, pure translation - no logic.
        """
        task = Task(state="completed")

        # Convert domain decision into A2A Artifact
        artifact = Artifact(content={
            "approved": decision.approved,
            "approver": decision.approver,
            "reason": decision.reason
        })

        task.artifacts.append(artifact)
        return task
Enter fullscreen mode Exit fullscreen mode

Why this adapter pattern works:

The AgentExecutor is now "boring" in the best way possible:

  • No business logic: It doesn't decide anything. It just translates.
  • Single Responsibility: It only knows how to convert between A2A and domain objects.
  • Easy to verify: You can verify the translation is correct without testing business rules.
  • Swappable: Want to use gRPC instead of A2A? Write a different adapter. Your domain logic stays identical.

Step 4: Testing Becomes Trivial

Now look at how testing works with proper separation:

# tests/test_expense_logic.py - Pure business logic testing

def test_cfo_approval_required():
    """
    Test business logic WITHOUT any A2A infrastructure.

    No servers. No protocol objects. No mocking.
    Just pure logic testing that runs in milliseconds.
    """

    # Create the service with test policies
    service = ExpenseApprovalService(policies={
        "cfo_threshold": 5000,
        "manager_threshold": 1000
    })

    # Create a domain object representing a large expense
    request = ExpenseRequest(
        amount=7500,  # Exceeds CFO threshold
        category="software",
        requestor="john@company.com",
        justification="Annual license"
    )

    # Call the business logic
    decision = service.evaluate(request)

    # Verify the business rule worked correctly
    assert decision.approved == False
    assert decision.approver == "CFO"
    assert decision.requires_escalation == True

def test_auto_approval():
    """
    Another test - still zero infrastructure needed.

    You can write dozens of these tests and run them all in seconds.
    Each test verifies a specific business rule.
    """
    service = ExpenseApprovalService(policies={
        "cfo_threshold": 5000,
        "manager_threshold": 1000
    })

    # Small expense should auto-approve
    request = ExpenseRequest(
        amount=500,  # Below all thresholds
        category="supplies",
        requestor="jane@company.com",
        justification="Office supplies"
    )

    decision = service.evaluate(request)

    # Verify auto-approval logic
    assert decision.approved == True
    assert decision.approver == "AUTO"

def test_manager_approval_required():
    """
    Testing the middle tier of approval.

    Notice: we're testing complex business logic without any
    understanding of A2A protocol, Messages, Tasks, or Artifacts.
    """
    service = ExpenseApprovalService(policies={
        "cfo_threshold": 5000,
        "manager_threshold": 1000
    })

    request = ExpenseRequest(
        amount=2500,  # Between manager and CFO thresholds
        category="travel",
        requestor="alex@company.com",
        justification="Conference travel"
    )

    decision = service.evaluate(request)

    assert decision.approved == False
    assert decision.approver == "MANAGER"
    assert decision.requires_escalation == True
Enter fullscreen mode Exit fullscreen mode

Why this testing approach is superior:

Compare this to the first version where business logic was in AgentExecutor:

  • First version testing: Create SendMessageRequest, construct Message with Parts, manage Task states, handle protocol objects. Each test takes seconds to run and requires infrastructure.

  • This version testing: Create ExpenseRequest, call service, check ApprovalDecision. Each test runs in microseconds and requires nothing but the code.

You can write comprehensive test coverage for all business rules without ever thinking about A2A protocol.

See the difference? Your business logic is completely isolated, fully testable, and has zero dependency on the A2A protocol.

What You Gain From This Architecture

Testable Intelligence

You can test your agent's core reasoning in milliseconds without any infrastructure:

def test_approval_rules():
    service = ExpenseApprovalService(policies={...})
    decision = service.evaluate(ExpenseRequest(...))
    assert decision.approved == True
Enter fullscreen mode Exit fullscreen mode

No A2A server. No protocol objects. No mocking. Pure logic testing.

Reusable Across Contexts

The same logic works everywhere:

# In an A2A agent
a2a_executor = ExpenseAgentExecutor(service)
result = await a2a_executor.execute(a2a_request)

# In a batch job
for expense in monthly_expenses:
    decision = service.evaluate(expense)
    save_to_database(decision)

# In a different protocol (gRPC, REST, etc.)
grpc_request = parse_grpc(request)
decision = service.evaluate(grpc_request)
send_grpc_response(decision)
Enter fullscreen mode Exit fullscreen mode

Evolvable Without Rewrites

Want to change a policy threshold? One line:

service = ExpenseApprovalService(policies={
    "cfo_threshold": 3000,  # Changed from 5000
    "manager_threshold": 1000
})
Enter fullscreen mode Exit fullscreen mode

No touching protocol code. No touching adapter code. Just pure business logic.

Understandable to Humans

A business analyst can read your agent core and understand what it does:

if request.amount > self.cfo_threshold:
    return ApprovalDecision(
        approved=False,
        approver="CFO",
        requires_escalation=True
    )
Enter fullscreen mode Exit fullscreen mode

No A2A concepts. No protocol knowledge required. Just business logic.


The Hexagonal Architecture Connection

If this pattern looks familiar, it should.

This is Hexagonal Architecture (also called Ports and Adapters), created by Alistair Cockburn in 2005 to solve one core problem: business logic keeps getting polluted by external concerns.

In traditional systems, those concerns were HTTP frameworks, database drivers, message queue clients, and UI libraries.

In agent systems, the external concerns are A2A protocols, streaming semantics, SDK objects, cloud platform APIs, and execution runtimes.

The principle remains identical: your core logic should not know how it's being called.

This matters even more for AI agents than for traditional services because the ecosystem evolves at a blistering pace:

  • Models change every few months
  • Frameworks rise and fall rapidly
  • Protocols evolve continuously
  • Requirements shift based on user feedback

Hexagonal architecture gives you one critical capability: the ability to change your mind about everything except your business logic.

The Real Numbers

Here's what the data shows from teams building production A2A agents:

Teams That Violate the Boundary

  • 6-12 months to production (includes multiple rewrites)
  • 3-4 weeks per major change
  • 40-60% of code is protocol handling mixed with logic
  • 6+ months of accumulated technical debt
  • Can't test logic without full infrastructure
  • Can't reuse across different contexts

Teams That Respect the Boundary

  • 2-3 months to production
  • 2-3 days per major change
  • 80% of code is reusable domain logic
  • Evolvable over years without rewrites
  • Tests run in milliseconds
  • Logic works in any context

The architecture decision happens on day 1. The consequences compound over 6-12 months.

That $500K expense approval agent? They're still dealing with the consequences six months later.

Your Architecture Health Check

Run these five checks on your A2A agent right now:

Check #1: Protocol Imports in Domain Code

grep -r "from a2a import" src/domain/
Enter fullscreen mode Exit fullscreen mode

Expected result: ZERO matches.

If you see imports: Your intelligence is protocol-coupled. Your domain layer knows about A2A. That's a boundary violation.

Check #2: Test Independence

Can you test your core logic like this?

def test_agent_logic():
    agent = MyAgentCore(config={...})
    result = agent.decide(domain_input)
    assert result.is_approved == True
Enter fullscreen mode Exit fullscreen mode

Expected result: Yes, without any A2A infrastructure running.

If you need A2A runtime to test: Your logic is not properly separated.

Check #3: Reusability

Try this thought experiment: "Could I use this agent's logic in a batch job tomorrow?"

Expected result: Yes, by just importing and calling your domain layer.

If you'd need to refactor: Your logic is coupled to the A2A runtime.

Check #4: Business Logic Location

grep -r "if amount >" src/adapters/
grep -r "policy" src/adapters/
grep -r "decision" src/adapters/
Enter fullscreen mode Exit fullscreen mode

Expected result: ZERO business logic in your adapters.

If you find business rules: You've violated the adapter pattern. Decision-making is happening in the translation layer.

Check #5: Framework Swappability

Ask yourself: "Could I replace LangChain with custom code in one day?"

Expected result: Yes, because your reasoning engine is an abstraction in your domain layer.

If you'd need a major rewrite: Your framework is hardwired into your architecture instead of being an implementation detail.

What To Do Next

If You're Starting a New A2A Agent

Follow this order before writing any code:

Step 1: Define Your Domain Layer First

Create your entities, services, and pure business logic. No A2A imports. No protocol objects. Just your domain model.

# Start here - not with AgentExecutor
class ExpenseApprovalService:
    def evaluate(self, request: ExpenseRequest) -> ApprovalDecision:
        # Your business logic
        pass
Enter fullscreen mode Exit fullscreen mode

Step 2: Write Tests for Domain Logic

Prove your logic works without any infrastructure:

def test_approval_logic():
    service = ExpenseApprovalService(policies={...})
    decision = service.evaluate(ExpenseRequest(...))
    assert decision.approved == True
Enter fullscreen mode Exit fullscreen mode

Step 3: Build the Adapter

Now create AgentExecutor as a pure translator:

class ExpenseAgentExecutor:
    def __init__(self, service: ExpenseApprovalService):
        self.service = service

    async def execute(self, request: SendMessageRequest) -> Task:
        domain_request = self._to_domain(request)
        decision = self.service.evaluate(domain_request)
        return self._to_a2a_task(decision)
Enter fullscreen mode Exit fullscreen mode

Step 4: Wire to A2A

Finally, connect the protocol layer. This should be the last step, not the first.

Start with architecture, not protocol.

If You Have an Existing A2A Agent

Here's your refactoring priority order:

Week 1: Extract Business Logic

Move decision-making code from AgentExecutor into pure domain services. Create domain entities that have no A2A dependencies.

Week 2: Create Domain Models

Replace A2A objects with domain-specific models. ExpenseRequest instead of Message. ApprovalDecision instead of Task.

Week 3: Write Domain Tests

Prove your logic works without protocol infrastructure. This validates your separation is real.

Week 4: Slim Down Adapter

AgentExecutor should now only translate. Remove all business logic. It should be thin and boring.

Week 5: Validate Boundary

Run automated checks to ensure NO A2A imports exist in your domain layer. Set up pre-commit hooks to prevent future violations.

Budget 2-3 weeks for this refactor if your agent is already in production. It will save you months of pain later.

The longer you wait, the harder the refactor becomes. Technical debt compounds.

Why Framework Names Don't Belong in Architecture Diagrams

Here's a quick litmus test for architectural clarity: Look at your architecture diagram. Does it mention LangChain? LangGraph? Specific cloud services by name?

If yes, you've confused implementation details with architectural structure.

LangChain is an implementation choice. Tomorrow you might use LangGraph. Next year you might build something custom. These are tools, not architectural layers.

The correct abstraction in your architecture diagram is "Reasoning Engine" optionally backed by an LLM. That layer might be implemented with any number of frameworks or none at all.

When you name specific frameworks in your core architecture, you're telling future maintainers that those frameworks are load-bearing walls. They're not. They're furniture. You should be able to move them without rebuilding the house.

Your architecture should be stable. Your implementation choices should be flexible.

The Bottom Line

A2A is not your agent. A2A is how your agent talks.

The A2A protocol solves the critical problem of agent-to-agent communication. It standardizes how agents discover each other, exchange tasks, stream updates, and coordinate work.

But communication is not intelligence.

Your agent's intelligence its business rules, decision logic, reasoning capabilities, memory, planning must live in a domain layer that knows nothing about communication protocols.

AgentExecutor is a protocol adapter. It translates between A2A's communication format and your domain's concepts. It does not make business decisions. It does not contain your logic. It translates.

This separation is not architectural perfectionism. It's not gold plating. It's not over engineering.

It's the difference between systems that evolve for years and systems that collapse after months.

The architecture decision you make today determines whether you're still maintaining this system in two years or completely rewriting it in six months.

That Fortune 500 company with the $500K agent? They're rewriting it now. Three weeks became two months. They're doing it right this time.

Don't be them. Get the architecture right from day one.

When A2A defines how your agents talk and your architecture defines how they think, you build systems that last.

When you mix these concerns, you build expensive lessons.

Choose wisely.

*Happy Architecting and Coding *

Thanks
Sreeni Ramadorai

Top comments (0)