All articles

Dynamics 365 Solution Design: Stop Putting Everything in One Solution

How you structure your Dynamics 365 solutions determines how painful your deployments will be. Here's a layered approach that actually scales.

· 9 min read

If you’ve inherited a Dynamics 365 environment that’s been running for a few years, there’s a good chance you’ve seen it: one solution called something like “Company Customizations” that contains every single component in the system. Tables, flows, apps, security roles, site maps — all of it, in one bucket.

Deploying that solution is a 45-minute gamble. Nobody knows what it’ll break. Nobody wants to touch it.

Here’s how to avoid building that, and how to think about solution architecture from the beginning.

The Core Principle: Segment by Lifecycle

The main question when deciding how to split solutions is: which components change together?

Components that change together should live in the same solution. Components with different deployment cadences should live in different solutions.

This sounds obvious until you’re in the middle of a project and someone says “just throw it in the main solution,” and you do, and six months later you regret it.

A Layered Solution Architecture

The pattern I recommend is three layers:

Layer 1: Foundation

Contains your data model and shared configuration:

  • Custom tables and columns
  • Relationships
  • Global option sets / choices
  • Connection references
  • Environment variables (definitions only, not values)
  • Security roles (base permissions)

This solution changes rarely. When it does change, it’s intentional and reviewed carefully. Managed in production.

Layer 2: Core Business Logic

Contains automation and process:

  • Cloud flows (Power Automate)
  • Business rules
  • Plugins (registered in this solution)
  • Custom APIs
  • Workflows (classic, if you’re still on them)

This changes more frequently than the foundation but is still gated. Changes here are tested thoroughly before deployment. Managed in production.

Layer 3: User Experience

Contains everything users see:

  • Model-driven apps
  • Canvas apps (if embedded)
  • Dashboards
  • Views
  • Forms
  • Charts
  • Site map

This changes most frequently. UX iterations happen fast. Having this separate means you can deploy a form update without touching your table definitions or flows. Managed in production.

Plus: Configuration Solution

A separate solution for environment-specific values:

  • Environment variable values
  • Connection references (actual values, not definitions)

This solution is unmanaged and deployed manually in each environment. It’s not part of your CI/CD pipeline — it contains the things that differ between environments by design.

Dependency Management

When you have multiple solutions, dependencies matter. Foundation is a prerequisite for Core Logic, which is a prerequisite for UX.

This means:

  • When deploying, always deploy in order: Foundation → Core Logic → UX
  • When you export, do it in the same order
  • Never create a component in UX that references something that should live in Foundation — it’ll force an implicit dependency

Power Platform displays dependencies in the solution explorer under Components → Missing Dependencies. Check this before every export.

Naming Conventions

Use a consistent prefix for your publisher and all components. If your publisher prefix is xyz:

  • Solutions: XYZ_Foundation, XYZ_CoreLogic, XYZ_UX, XYZ_Config
  • Tables: singular noun — xyz_customerrequest, not xyz_customerrequests
  • Columns: describe what it contains — xyz_approvaldate, not xyz_field1. Booleans as questions: xyz_isapproved, xyz_requiresreview
  • Flows: [XYZ] Action - Trigger — e.g., [XYZ] Send Email - Order Created
  • Apps: [XYZ] App Name — e.g., [XYZ] Customer Service

The prefix in component names is permanent. You can’t change it after creation without recreating the component. Choose it carefully.

Avoid crm as a prefix — Microsoft’s own Dynamics 365 components use crm internally. Using it for your custom components creates confusion when reading schema names.

Connection References and Environment Variables

Two components that should be in every solution from day one:

Connection references replace direct connector connections in your flows. Instead of a flow being tied to “John’s Outlook connection,” it uses a reference that gets remapped during deployment. Name them clearly: xyz_SharedDataverse, xyz_OutlookShared. Every flow that touches a connector should use one — otherwise your deployments will break when the original connection isn’t available.

Environment variables store values that differ between environments: API URLs, notification email addresses, feature flags, thresholds. Define the variable in your Foundation solution; set the value in your Configuration solution. Never hardcode environment-specific values in flows or apps.

What About Canvas Apps?

Canvas apps are a special case. They don’t participate in the solution dependency graph the same way model-driven components do. A canvas app that connects to a Dataverse table doesn’t create a formal dependency that shows up in the solution checker.

This means it’s easy to accidentally break a canvas app deployment by changing a column it relies on. My approach:

  • Keep canvas apps in the UX solution
  • Document Dataverse dependencies manually (or use the App Checker in Power Apps Studio)
  • Before changing a column used by canvas apps, search for it using the dependency tool

Managed vs. Unmanaged: A Quick Reminder

Unmanaged solutions in non-production environments are fine — you need them to make changes. But what gets deployed to production should always be managed. Managed solutions:

  • Can be uninstalled cleanly
  • Prevent direct edits in production (a feature, not a bug)
  • Support upgrade and update flows

If your production environment has unmanaged customizations sitting on top of managed solutions, that’s technical debt. At some point you’ll need to reconcile them.

Solution Upgrade vs. Update

When deploying a new version:

  • Update: applies changes from the new version on top of the existing managed solution. Faster, but can leave orphaned components if you deleted something in the new version.
  • Upgrade: fully replaces the old solution with the new one. Slower, but clean. Orphaned components get removed.

Use Update for small incremental changes. Use Upgrade when you’ve deleted or significantly reorganized components. Always test the upgrade path in a staging environment first.

One More Thing: Publisher Matters

Every solution has a publisher. Every component inherits the publisher’s prefix. If you have multiple publishers in your environment, you’ll end up with components with different prefixes — which is a mess to maintain and support.

Use one publisher per customer/environment. If you’re an ISV, use one publisher for your product. Don’t let the default “Default Publisher” touch anything you actually care about.

One more thing — if you’re still exporting and importing solution zips manually, look at the Power Platform CLI (pac). pac solution clone, pac solution export, pac solution import from the command line. Combined with pac solution unpack for source control, it makes the whole process scriptable. Once you’ve tried it, you won’t go back to the portal.

Share this article LinkedIn X / Twitter

Related articles