Dataverse solutions give you the ability to package new and existing Power Platform objects in one environment and move them to another once they're ready to test or use. This approach is called Application Lifecycle Management (ALM).
Before we get into the weeds on this topic, let's define everything we'll be talking about.
Power Platform ALM Core Components
Environments - The highest level in the Power Platform hierarchy
Types (there are other types you can review here)
Sandbox - an environment for development. Has options to copy and reset the environment.
Production - an environment with developed solutions where users interact with your business data
Solutions - Containers within an environment that hold objects or object references
Unmanaged - Can be edited. Deleting will remove the solution but not the objects within it
Managed - Cannot be edited. Deleting will remove the solution and all of its objects.
Default Solution - An unmanaged solution included in all environments that holds all objects in the environment.
Solution Objects - The components contained in a solution. These can be anything from a Power App or a Dataverse Table to custom JavaScript or Environment Variables.
Solution Action - Determines the behavior of the solution import (applies to managed solutions only)
Upgrade - The default action for a managed solution. Objects missing from the new version will be removed, and existing objects will be updated.
Stage for upgrade - Brings in new objects from the solution, but defers the removal of missing objects until you apply the upgrade.
Update - The default action for an unmanaged solution (the only action an unmanaged solution can perform). Brings in new solution objects, but does not remove missing objects.
Let's begin with the assumption that you have a brand new Sandbox and Production environment with no custom solutions. Let's create a solution in the sandbox environment.
To create a solution, you need the System Customizer or System Administrator security role in the environment (or a custom role that allows solution creation).
We'll create a solution called Solution A. As we create this solution we'll need to choose a publisher. A publisher can tell you who the solution belongs to, whether it's an individual, team, or a project. All objects created within a solution will have a prefix that is associated with that publisher. Publishers don't do much beyond that, but they are a required piece to add while creating a solution. The best practice is to always create a custom publisher even if it's a generic publisher for your entire organization. For more information, see the publisher documentation here.
We'll use the example publisher below. In the New Solution pane, click New Publisher under the Publisher dropdown.
We now have an unmanaged solution in our sandbox environment! From here, let's create an object. When creating new objects, it's good practice to create them from within a solution. Adding an existing object to a solution that was created outside a solution can cause issues with canvas apps and Power Automate flows (this is a bug and will likely be resolved one day. As far as I know, there are no other issues with adding existing objects). Let's add a table to this solution.
We'll call it Table A
We now have an unmanaged solution called Solution A in a sandbox environment using the My Publisher publisher that contains an object (a table in our case) called Table A.
Create another solution, Solution B, and add a new table to it, Table B.
Here is a diagram of your environments at this stage:
Note that the tables we created were automatically added to the Default Solution. If we were to try to delete Table A or Table B from their respective solutions, we would be asked if we want to remove them from the solution or the environment entirely. This is because the table in the solution is a pointer to the actual table object.
If Table A is in both solutions and you make a change to the table itself (such as the name) while you are in Solution A, the change is made to the table at the environment level, so that change is reflected in both solutions. This is the concept I struggled with the longest. Separate solutions do not contain separate versions of the objects they hold, they simply contain a reference to the object in that environment.
However, if you add a new column to Table A in Soution A, that column will not automatically be referenced in Solution B. This is because the column is a separate solution object. All solution objects are referenced in the Default Solution, so the new column will be referenced automatically in the Default Solution.
Also, note that our solutions exist only in the sandbox environment and not in production. Next, we'll export and import Solution A from the sandbox into production.
Navigate to Solution A's Overview tab in the left pane, then click Export in the top option bar. Leave all settings as default on the first page of the export panel, then on the second page select Unmanaged under the Export as section, then click Export.
The export panel will close and you should see a notification on the solution overview page that says Currently exporting solution "Solution A".
After some time, the notification will turn green and the notification will say Solution "Solution A" exported successfully along with a download button.
Click download to download the unmanaged solution's zip file.
For some version control, save this zip file in a SharePoint document library or OneDrive folder so that you can revert if something goes wrong. There are much more robust ways of handling versioning, this concept is called Source Control. I won't dive into that here, but you can read more about your options here.
Now, in our production environment, navigate to the solutions section and select Import solution.
Click the Browse button, select your zip file, then click Next, and finally click Import.
After the solution finishes importing, our environments look like this:
Now, let's export Solution B, but this time, select Managed under Export as:
Notice the zip file indicates the solution is managed. Now, let's import it into our production environment following the same steps as the unmanaged solution above. After that, our environments will look like this:
Notice that even though it is unmanaged, the Default Solution contains a managed object from Solution B. If we were to delete Solution A and Solution B from our production environment, our environments would look like this:
Note that even though we deleted the unmanaged Solution A, the object stayed behind in the Default Solution, whereas when we deleted the managed Solution B, the object was removed.
In a real-world scenario, managed solutions are usually used for distributing solutions to external users, while unmanaged solutions are generally used for development and internal use. There is no set-in-stone right or wrong way to do this. Some organizations require all production solutions to be managed, but it just depends on how you want to set this up.
You can also set up a series of Environments so that you have a sandbox for development, then those solutions can move to a sandbox environment for testing, and then ultimately production. Unmanaged solutions can be exported as managed from any environment, even if they were imported as unmanaged. Managed solutions cannot be exported as unmanaged.
One last concept I'd like to talk about is Solution Layering. Solution layers are created within an object when the same solution object is imported via a managed and unmanaged solution. This is how Dataverse handles varying object versions when they exist in multiple managed solutions.
The unmanaged solution layer (essentially the Default Solution) always trumps managed layers. For example, if Table A existed in both unmanaged Solution A and managed Solution B, then once both solutions were imported, there would be one Table A with two solution layers, a managed layer from Solution B and an unmanaged layer from Solution A.
This means if I change the name of the table in the managed Solution B in the sandbox and import it into production, those changes are not seen in production, as they are covered by the unmanaged layer where those changes have not been made.
Solution layers are not an issue if all solutions are unmanaged. In that case, the most recent change to a solution object is set during import. Solution layers can exist on all solution objects, so not just a table, but a table column. Managed layers go in order of import. The unmanaged layer can be removed, which reverts the object back to its managed state. This should only be done in rare cases, mostly when unmanaged layers were created in error.
Note: There is currently a bug that allows managed Power Automate flows and Canvas Apps to be edited. Editing a managed object introduces an unmanaged solution layer.
Now that you have all the pieces required, experiment on your own with exporting and importing unmanaged and managed solutions.
Add new or existing objects to Solution B and re-export (which will increment the version number) and then import again and see how the objects in production behave. Remove Table B from Solution B, export and re-import the managed solution to production, and see what happens.