Enterprise Power Automate deployments collapse when configuration, shared utilities, and business logic are bundled into a single solution. This lesson teaches you to design a three-layer solution architecture — configuration, shared components, and business logic — that makes deployments targeted, dependencies explicit, and maintenance predictable at scale.

Imagine you're six months into a successful Power Automate rollout. You have 40 flows spread across a handful of solutions, your dev team is shipping new automations weekly, and everything feels fine — until the day you need to update the base URL for your ERP system. Suddenly you're hunting through 23 different flows, updating environment variables in six different solutions, and hoping you didn't miss any. The next morning, three production flows fail because they were pulling the old endpoint from a hardcoded string that someone forgot to externalize.
This is what happens when you build automation at scale without deliberate solution architecture. The problem isn't that Power Automate is hard — it's that the platform gives you just enough flexibility to make locally rational decisions that become globally catastrophic. The fix isn't discipline alone; it's structure. Specifically, it's a layered solution architecture where configuration lives separately from shared components, which live separately from business logic, each managed and deployed as its own tier.
By the end of this lesson, you'll understand how to design that architecture from scratch, why each layer exists, how managed solutions enforce the boundaries between them, and how to promote changes across environments without breaking the things that depend on each layer.
What you'll learn:
You should be comfortable with the basics of Power Automate solutions — what a solution is, why you'd put flows inside one rather than leaving them unmanaged, and the difference between managed and unmanaged solutions. If you need a refresher on that foundation, the article on solution architecture for Power Automate: publishers, managed vs unmanaged, and dependencies covers those concepts in depth. You should also understand the purpose of separate environments — if dev, test, and production feel interchangeable to you right now, read up on environment strategy for Power Platform first.
Before we build the layered model, let's understand precisely why the single-solution approach breaks down. Think of it like a software monolith: it works fine for one developer over six months. But when you have multiple teams, multiple release cadences, and components that are reused across business domains, a monolith becomes a liability.
In Power Automate terms, a monolithic solution typically bundles everything together: the SharePoint URL for your department portal, the child flow that formats currency amounts, the approval flow for purchase orders, and the notification flow for IT helpdesk tickets. These four things have completely different change rates. The SharePoint URL almost never changes. The currency formatting logic changes occasionally when business rules shift. The approval flow changes frequently as purchasing policy evolves. The notification flow is owned by a different team entirely.
When all four live in one solution, any update to any component requires you to export, re-import, and validate the whole package. Worse, when you import a managed solution update into production, you risk overwriting configuration values that your operations team has already tuned for that environment. You have no way to let the IT team deploy their notification flow independently of the purchasing team's approval flow. You've accidentally coupled unrelated things.
The layered model solves this by treating each concern as a separately deployable unit, with explicit dependencies flowing downward: business logic depends on shared components, shared components depend on configuration, but never the other way around.
Key insight
Dependency direction matters enormously. Configuration should know nothing about the flows that use it. Shared components should know nothing about which business process called them. Only business logic is allowed to reference the other layers — never the reverse.
Let's define each layer precisely, using a realistic scenario: an enterprise HR automation system that handles employee onboarding, offboarding, and internal transfer requests across Microsoft 365, a Workday HRS integration, and a custom ticketing system.
The configuration layer holds everything that changes based on where you're running, not what you're doing. This layer answers the question: "What environment-specific facts does everything else need to know?"
For our HR system, that includes:
Notice what's not in the configuration layer: no flows, no logic, no process definitions. Just data and references.
In practice, you create a solution in your dev environment — call it Contoso_HR_Configuration_dev — containing only environment variable definitions and connection reference definitions. When you export this as a managed solution and import it into test, the import process prompts you to supply the test-appropriate values for each variable. The same happens when you promote to production.
This is the critical benefit of isolating configuration: when your Workday tenant URL changes in production, your operations team opens the configuration solution's environment variables, updates the value, and nothing else needs to be redeployed. The flows that reference contoso_workday_base_url automatically pick up the new value on their next run.
Tip
Name your environment variable schema names with a publisher prefix and a clear domain prefix, like contoso_hr_workday_base_url. This prevents collision with variables from other solution publishers and makes it obvious which layer and domain owns each variable.
The shared components layer contains reusable building blocks that multiple business processes depend on. These are things you'd write once and call many times — the equivalent of a shared library in traditional software development.
For our HR system, shared components include:
Format-EmployeeDisplayName, Send-HRNotification, Validate-ADUserExists, Create-TicketingSystemRecordThe shared components layer references assets from the configuration layer (it uses the contoso_hr_ticketing_api_url environment variable when calling the ticketing system) but knows nothing about which onboarding flow or offboarding flow will call it.
The practical implementation is a solution called Contoso_HR_SharedComponents. When you build this solution, you add the environment variable references (not definitions — the definitions live in Layer 1) as dependencies. This is how Power Platform's managed solution dependency tracking works: a solution can consume an asset defined in another solution, and the platform enforces that the defining solution is present before the consuming solution can be imported.
Orchestrating child flows and scoped execution is the core skill for building this layer well. Your shared component flows should be designed with explicit input and output schemas, proper run-only permissions, and no hardcoded values.
Warning
Do not put connection references in the shared components layer if they're also needed by business logic flows. Connection references should always be defined in the configuration layer and referenced by both layers above it. Defining the same connection in two separate managed solutions creates update conflicts that are painful to resolve.
The business logic layer is where actual process automation lives. Each business domain or process family gets its own solution here:
Contoso_HR_OnboardingContoso_HR_OffboardingContoso_HR_TransfersEach of these solutions contains the flows, approvals, and process-specific Dataverse tables that implement a particular HR workflow. The onboarding solution references the Send-HRNotification child flow from the shared components layer and uses the contoso_hr_workday_base_url environment variable from the configuration layer.
Crucially, each business logic solution can be deployed and updated independently. When the purchasing department changes approval routing rules, you export and import only Contoso_HR_Onboarding — you don't touch configuration or shared components. This is what makes the model genuinely useful: it turns a whole-system deployment into a targeted, low-risk operation.
Understanding the concept is one thing. Understanding how managed solutions technically enforce layer boundaries is what makes this work reliably across a team.
When you export a solution as managed, it becomes read-only in the target environment. You cannot edit managed flows in the production environment — they can only be changed by importing a new version of the managed solution. This is exactly what you want for the configuration and shared components layers: no one should be hand-editing a shared child flow in production.
The dependency system works like this: when you add an environment variable reference to a flow inside Contoso_HR_Onboarding, the platform records that Contoso_HR_Onboarding depends on the environment variable contoso_hr_workday_base_url, which is defined in Contoso_HR_Configuration. If someone tries to import Contoso_HR_Onboarding into an environment that doesn't already have Contoso_HR_Configuration installed, the import fails with a dependency error. The platform physically prevents out-of-order deployment.
Note
The dependency graph only works correctly when you consistently use connection references and environment variables rather than embedding connection details or values directly in flow expressions. A hardcoded URL in an expression is invisible to the dependency tracker — the platform has no way to know that expression value matters.
This is also why securing Power Automate flows with connection references is not just a security practice but an ALM practice. Connection references defined in the configuration layer and referenced upward create the dependency chain that ensures correct deployment ordering.
The deployment sequence is therefore always:
Contoso_HR_Configuration (managed) → supply environment-specific valuesContoso_HR_SharedComponents (managed) → dependencies on Layer 1 satisfiedContoso_HR_Onboarding, Contoso_HR_Offboarding, Contoso_HR_Transfers (managed, in any order) → dependencies on Layers 1 and 2 satisfiedLet's get specific about how you actually build Layer 1. In your development environment, navigate to make.powerapps.com, switch to your dev environment, and open the Solutions area.
Create a new solution with a meaningful display name like Contoso HR - Configuration and a unique name like Contoso_HR_Configuration. Set the publisher to your organization's standard publisher (not the default publisher — using a consistent publisher prefix is essential for namespace hygiene across all layers).
Inside this solution, you create environment variable definitions. For each variable, you specify:
contoso_hr_workday_base_urlYou also create connection reference definitions here. A connection reference is a named pointer to a connector type — for example, a connection reference for SharePoint. The actual credential binding happens during import into each environment, but the reference definition lives in this solution.
For the feature flag pattern, use Boolean environment variables. A variable like contoso_hr_new_onboarding_checklist_enabled with a default of false lets you enable the feature in test before production by simply updating the variable value — no code change, no redeployment.
Tip
Define a contoso_hr_support_email string variable in the configuration layer and reference it anywhere an error notification or escalation needs to reach a team. When your HR team's email group changes, one variable update propagates across every flow that uses it — no hunting through action configurations.
The detailed mechanics of how environment variables behave during deployment, including how to use deployment settings files to supply non-default values automatically in a CI/CD pipeline, are covered in the lesson on defining and enforcing environment variable strategies.
With Layer 1 in place, build Contoso_HR_SharedComponents. The primary assets here are child flows.
Each child flow in this layer should follow a consistent contract pattern. Take the Send-HRNotification child flow as an example. Its trigger is "Manually trigger a flow" with defined inputs:
recipient_email (text) — who receives the notificationnotification_type (text) — maps to a template selectoremployee_display_name (text) — personalization dataaction_url (text, optional) — deep link for the recipient to take actionThe flow uses the contoso_hr_support_email environment variable (referenced from Layer 1) for the reply-to address. It returns a notification_sent boolean and a message_id string as outputs.
This explicit contract is what makes the shared component genuinely reusable. Any business logic flow can call it without knowing how email delivery works internally. If you later switch from Office 365 Outlook to SendGrid, you update Send-HRNotification in one place and redeploy Contoso_HR_SharedComponents — all three business logic solutions get the new behavior without any changes of their own.
When you add these child flows to the Contoso_HR_SharedComponents solution, the solution automatically picks up the environment variable references as dependencies. When you export this solution as managed, those dependencies are recorded in the solution manifest.
Each business logic solution is the most familiar territory — it's where the actual process flows live. But there are specific disciplines that keep this layer clean.
Never define environment variables here. If a business logic flow needs a configuration value, that variable must exist in Layer 1. If it doesn't exist yet, add it to Layer 1 first, redeploy Layer 1, then reference it in your business logic.
Never duplicate shared logic. If you find yourself writing the same "check if AD user exists" logic in both the onboarding flow and the transfer flow, that logic belongs in a shared component child flow. Resist the urge to copy-paste within the business logic layer — it defeats the maintenance benefit.
Reference, don't re-implement. Your onboarding flow should call Send-HRNotification rather than containing its own send-email action configured with the same parameters. The connection reference for Office 365 Outlook should be defined in Layer 1 and referenced in Send-HRNotification — the onboarding flow should never directly hold a connection reference for email if the shared component already handles it.
When building flows that integrate with external APIs, custom connectors and HTTP actions belong in the shared components layer when multiple business processes need the same API, and in the business logic layer when they're specific to a single process.
Work through this exercise to cement the concepts. You'll need access to a Power Platform dev environment.
Scenario: You're building an IT request automation system with two processes: a hardware request approval flow and a software license request flow. Both need to notify a shared IT helpdesk email address and call the same ticketing system API.
Step 1: Create the configuration solution.
Create a new solution named Contoso_IT_Configuration. Add three environment variables:
contoso_it_helpdesk_email (String, default: your dev email address)contoso_it_ticketing_api_url (String, default: https://dev-tickets.contoso.com/api)contoso_it_max_approval_days (Number, default: 5)Add a connection reference for Office 365 Outlook. Export this solution as managed and import it into a second test environment (or note the export for later review).
Step 2: Create the shared components solution.
Create a new solution named Contoso_IT_SharedComponents. Build a child flow named Send-ITHelpdesk-Notification with inputs for recipient_email, subject, and body. Inside the flow, use the contoso_it_helpdesk_email environment variable as the "From" address and the connection reference from Layer 1 to send the email. Add this flow to your solution.
Step 3: Create a business logic solution.
Create Contoso_IT_HardwareRequests. Build a simple approval flow triggered by a SharePoint list item creation. When the item is approved, call Send-ITHelpdesk-Notification with appropriate parameters. When the item is rejected, call it again with a rejection message.
Verify the dependency chain: In your Contoso_IT_HardwareRequests solution, navigate to Solution → Components and look at the solution details. Confirm that the environment variables and child flow appear in the dependency list. This is the platform acknowledging the layer structure you've built.
Mistake: Putting environment variables in every solution. You'll see solutions where each business logic solution defines its own environment variables. This means when the Workday URL changes, you have to update it in six places. Consolidate all configuration into Layer 1.
Mistake: Defining connection references in business logic solutions. When connection references are scattered across multiple solutions, you end up with import conflicts and authentication gaps. If two solutions both define a connection reference for SharePoint, the second import may not correctly bind to the existing connection. Keep all connection references in the configuration layer.
Mistake: Deploying layers out of order. If you try to import the shared components solution before the configuration solution exists in the target environment, the import will fail citing missing dependencies. Always deploy in Layer 1 → Layer 2 → Layer 3 order. Automate this sequence in your CI/CD pipeline so it's never a manual judgment call. The lesson on deploying and managing Power Automate solutions across environments walks through automating this sequence end-to-end.
Mistake: Building shared components that are too specific.
A child flow named Send-Onboarding-Welcome-Email is not a shared component — it's business logic that happens to send email. A shared component named Send-HR-Notification that accepts the email content as a parameter is genuinely reusable. If your shared component references a specific business process concept, it belongs one layer up.
Troubleshooting: "Missing dependency" error on import. Open the error details — the platform names the missing dependency. Confirm that the solution defining that dependency is already installed and managed in the target environment. If the dependency is an environment variable, check that its schema name matches exactly (these are case-sensitive in some scenarios).
Troubleshooting: Environment variable shows "no value" in production. This happens when you imported the configuration solution without setting environment values for that environment. Navigate to the configuration solution in production, find the environment variable, and set the current value. The "current value" overrides the "default value" per-environment without modifying the managed solution — this is the designed behavior.
Warning
Never set the "default value" of an environment variable to a production-specific value in your dev environment definition. The default travels with the managed solution and can accidentally overwrite carefully-set production values if you later reimport with a new default. Always use "current value" for environment-specific overrides.
The three-layer model — configuration, shared components, business logic — transforms Power Automate from a collection of ad-hoc automations into a maintainable, enterprise-grade system. Configuration holds the environment-specific facts. Shared components hold the reusable logic. Business logic holds the process automation. Managed solution packaging enforces boundaries by making lower layers read-only in target environments and by tracking dependencies so deployment order is explicit and validated.
The practical discipline is straightforward once you internalize the direction of dependencies: always flow downward. Business logic can reference shared components and configuration. Shared components can reference configuration. Nothing references upward. This single rule, applied consistently, prevents the coupling that makes large automation portfolios expensive to maintain.
Your next steps from here: