Why going around in circles with Power Platform solution dependencies hurts

Understanding why circular dependencies are problematic but easy to create and how to avoid them.

cover

What are component dependencies?

Whenever you create a solution-aware component in Power Platform (Dataverse), there are often other components that that component needs to function. These are the dependencies of the component.

For example:

  • if you create a view, it will depend on each of the columns that you’ve used to display or for the filters. Those columns must be there for the view to work.

  • if you create a relationship it will depend on the target table that it points at. That table must be there for the relationship to work.

You can see the dependencies for any component in the Power Apps/Automate maker portal, by selecting the “Show dependencies” action:

The “Uses” tab shows the outgoing dependencies on which the current component depends.

The “Used by” tab shows the incoming dependencies - the components that depend on the current component.

The dependencies are displayed grouped by the solution that provides (the base layer of) the component. This will always be “Unmanaged solution” for unmanaged components, but for managed components, this will display a specific solution name.

Solution dependencies

When you export a solution, Dataverse creates a list of all the components on which components in the current solution depend but which aren’t also present in that same solution. These are stored in the MissingDependencies element in the solution.xml manifest inside the solution.

When you try to import a solution this list is used as a quick way to check that all of the required components are present. If any of these are missing, the import will fail.

We can summarise the other solutions a solution depends on which others by summarising the solutions that appear in this list.

So if the current solution “Solution A” depends on 3 components in “Solution B” and 4 in “Solution C”, we can summarise that Soluton A depends on B and C.

Note: Strictly speaking, the actual solution which provides each component is not significant as the platform does not enforce this. We could provide Table 4 via a different solution and as long as the component is present at the time of check, the platform does not care that it didn’t come from “Solution C” as expected. The solution names are shows simply as a convininece to help us.

So what are circular solution dependencies?

Circular solution dependencies are when we create a set of solutions that all circularly depend on each other, directly or indirectly based on the components in each solution.

Here’s the most simple case of circular dependencies between the components in 2 solutions:

Each has a component that directly depends on the other. So the dependencies are circular; A depends on B which depends on A.

But circular dependencies don’t have to be this simple. We might have more than 2 solutions involved, or other unrelated components in the same solutions that are causing the dependencies. It’s about the overall dependencies for the solution rather than for individual components. In this example, there is a circular dependency between all of these solutions, but caused by different components:

Overall in this example, there is a circular dependency between the solutions A=>B=>C and then back to A. It’s caused by how we’ve organised the solutions - we could move things around to avoid this. Note that on a component level in this example, there is no circular dependency.

It’s easy to create circular dependencies

As you can see from the examples above, it’s trivial to create circular dependences and they can’t be created in so many different ways. The platform doesn’t do anything to help us identify and/or avoid them.

Why are circular dependencies bad?

Circular dependencies create a set of solutions we cannot install

Circular dependencies create a chicken and egg problem; Which can be installed first?

None of them!

The problem is, we can’t install any of them first (into a clean environment - see below) because they depend on the others:

  • Solution A needs B so we can’t install it.

  • Solution B needs A so we can’t install it

We are stuck!

Circular dependencies create a set of solutions we can’t remove or evolve

The chicken and egg problem doesn’t stop there.

Not only does it make it impossible to install the solutions. It also makes it impossible to remove the solutions. If solution A depends on B and vice-versa, we cannot remove either of them directly.

This also plays out sometimes when you are trying to evolve your solutions by removing components or moving them around. The circular dependencies at a component level being split across multiple solutions can prevent the solution upgrade from succeeding.

Why circular dependency problem can be hidden

There is sometimes a caveat that can hide the circular dependencies problem which is often the reason many teams don’t realise they have created this problem until well into the future:

Let’s say you have v1 of your solutions that already contain tables A and B, but no circular relationship between them (v1):

You deploy this to your test environment. No problem.

Now you create the relationship between B and A (v2):

You now deploy to the environment that already has v1.

Because B and A are already present in this environment, deployment will succeed. (In this case, it will work in either order).

The problem with this is that since deploying to environments that don’t already have the preceding version comes much less frequently, you might not hit these issues until it’s too late. That could be when you’re trying to deploy to your production environment (which is updated less frequently and never got v1 deployed at all), or much later when you are trying to refactor/remove your solutions.

Nobody wants to spend an extra 4 hours working out how to untangle such a problem during the production go-live! Never mind the risks involved if they try to solve it ad-hoc and invalidate all the testing that has been done.

So… summary… Whilst you might be able to get away with this a lot of the time… it’s best to avoid creating solutions with circular dependencies at all if you want to avoid this pain!

Recovering an environment from circular solution dependencies

Once you’ve created a circular dependency situation in an environment, it can be complex to work out how to recover from it.

Microsoft has documented one example here:

https://learn.microsoft.com/en-us/troubleshoot/power-platform/dataverse/working-with-solutions/circular-dependencies-between-solutions

So recovering from these situations is a multi-step process. If you’re having to do this as part of your prod deployment etc (because you only just found out you have this issue), it’s a very dangerous thing to be doing without testing.

Untangling circular solution dependencies

Avoidance is a good tactic

The number one way to avoid dependency issues, in general, is to simply avoid breaking your solution up into multiple solutions and instead use a single solution, or at least fewer solutions.

Here’s the scenario above, but using a single solution:

Now there’s no problem. With everything inside a single solution, all the components that depend on each other are inside that single solution and there are no external solution dependencies to worry about.

If we did want to still segment into multiple solutions, it could look like this:

Now again, we have no problem, because moving some of the tables has removed the inter-solution dependencies.

When avoidance won’t work, strictly understand and control dependencies

Create a dependency-ordered list of your solutions.

The solutions at the top of the list must never depend on solutions lower down the list.
e.g.

  1. Solution A

  2. Solution B

  3. Solution C

So:
Solution A must have no dependency on B or C
Solution B can depend on A but not C
Solution C can depend on A and/or B

I f you have an existing issue, you might need to introduce new solutions to make the above rule work

Understand what segmentation strategies can actually work

It’s important to realise that it’s very hard to respect these rules if you try to follow hard and fast rules like “all tables in solution A, all Flows in solution B”.
Instead, you must segment based on the actual dependencies between the components. These change rapidly as your development moves on and also as the platform continues to develop, introducing new combinations of dependencies that can occur between component types. It is often easier to segregate only functional/module boundaries instead.

When deploying, always install the solutions in the defined order .

If you can, use a separate development environment for each solution and install only the solutions earlier in the list as using a managed solution

This ensures that it is impossible to create dependencies in the wrong direction. The downside is that this involves a lot of ongoing effort.

If you choose not to do this:

  • Frequently validate that your solutions can be installed in a clean environment.

or

  • Inspect the dependencies frequently to check you haven’t introduced any dependencies that break the rules.
    It is possible to automate this check with some effort.