diff --git a/src/Microsoft.PowerPlatform.PowerApps.Persistence/Microsoft.PowerPlatform.PowerApps.Persistence.csproj b/src/Microsoft.PowerPlatform.PowerApps.Persistence/Microsoft.PowerPlatform.PowerApps.Persistence.csproj
new file mode 100644
index 00000000..cfadb03d
--- /dev/null
+++ b/src/Microsoft.PowerPlatform.PowerApps.Persistence/Microsoft.PowerPlatform.PowerApps.Persistence.csproj
@@ -0,0 +1,9 @@
+
+
+
+ net7.0
+ enable
+ enable
+
+
+
diff --git a/src/Microsoft.PowerPlatform.PowerApps.Persistence/Models/BuiltInTemplatesUris.cs b/src/Microsoft.PowerPlatform.PowerApps.Persistence/Models/BuiltInTemplatesUris.cs
new file mode 100644
index 00000000..f64d02af
--- /dev/null
+++ b/src/Microsoft.PowerPlatform.PowerApps.Persistence/Models/BuiltInTemplatesUris.cs
@@ -0,0 +1,11 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+namespace Microsoft.PowerPlatform.PowerApps.Persistence.Models;
+
+internal static class BuiltInTemplatesUris
+{
+ public static readonly Uri Screen = new("http://microsoft.com/appmagic/screen#screen");
+ public static readonly Uri Button = new("http://microsoft.com/appmagic/button#button");
+ public static readonly Uri Label = new("http://microsoft.com/appmagic/label#label");
+}
diff --git a/src/Microsoft.PowerPlatform.PowerApps.Persistence/Models/Button.cs b/src/Microsoft.PowerPlatform.PowerApps.Persistence/Models/Button.cs
new file mode 100644
index 00000000..17935014
--- /dev/null
+++ b/src/Microsoft.PowerPlatform.PowerApps.Persistence/Models/Button.cs
@@ -0,0 +1,14 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+namespace Microsoft.PowerPlatform.PowerApps.Persistence.Models;
+
+[FirstClass(Template)]
+internal record Button : Control
+{
+ internal const string Template = "Button";
+ public Button()
+ {
+ ControlUri = BuiltInTemplatesUris.Button;
+ }
+}
diff --git a/src/Microsoft.PowerPlatform.PowerApps.Persistence/Models/Control.cs b/src/Microsoft.PowerPlatform.PowerApps.Persistence/Models/Control.cs
new file mode 100644
index 00000000..e8f4c27e
--- /dev/null
+++ b/src/Microsoft.PowerPlatform.PowerApps.Persistence/Models/Control.cs
@@ -0,0 +1,30 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+namespace Microsoft.PowerPlatform.PowerApps.Persistence.Models;
+
+//TODO: abstract?
+internal record Control
+{
+ // TODO: rename to "Control" in yaml
+ // TODO: make this a string and handle parsing/matchin later
+ ///
+ /// template uri of the control.
+ ///
+ public Uri? ControlUri { get; init; }
+
+ ///
+ /// the control's name.
+ ///
+ public string? Name { get; init; }
+
+ ///
+ /// key/value pairs of Control properties. Mapped to/from Control rules.
+ ///
+ public IReadOnlyDictionary? Properties { get; init; }
+
+ ///
+ /// list of child controls nested under this control.
+ ///
+ public Control[]? Controls { get; init; }
+}
diff --git a/src/Microsoft.PowerPlatform.PowerApps.Persistence/Models/ControlProperty.cs b/src/Microsoft.PowerPlatform.PowerApps.Persistence/Models/ControlProperty.cs
new file mode 100644
index 00000000..5fb5c995
--- /dev/null
+++ b/src/Microsoft.PowerPlatform.PowerApps.Persistence/Models/ControlProperty.cs
@@ -0,0 +1,14 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+namespace Microsoft.PowerPlatform.PowerApps.Persistence.Models;
+
+// TODO
+//internal sealed record ControlProperty
+//{
+// public string Name { get; init; }
+
+// public string? Value { get; init; }
+
+// public bool IsFormula { get; init; }
+//}
diff --git a/src/Microsoft.PowerPlatform.PowerApps.Persistence/Models/CustomControl.cs b/src/Microsoft.PowerPlatform.PowerApps.Persistence/Models/CustomControl.cs
new file mode 100644
index 00000000..228ff8ef
--- /dev/null
+++ b/src/Microsoft.PowerPlatform.PowerApps.Persistence/Models/CustomControl.cs
@@ -0,0 +1,6 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+namespace Microsoft.PowerPlatform.PowerApps.Persistence.Models;
+
+internal record CustomControl : Control;
diff --git a/src/Microsoft.PowerPlatform.PowerApps.Persistence/Models/FirstClassAttribute.cs b/src/Microsoft.PowerPlatform.PowerApps.Persistence/Models/FirstClassAttribute.cs
new file mode 100644
index 00000000..94d16f91
--- /dev/null
+++ b/src/Microsoft.PowerPlatform.PowerApps.Persistence/Models/FirstClassAttribute.cs
@@ -0,0 +1,14 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+namespace Microsoft.PowerPlatform.PowerApps.Persistence.Models;
+
+[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false)]
+internal class FirstClassAttribute : Attribute
+{
+ public FirstClassAttribute(string? nodeName = null)
+ {
+ NodeName = nodeName ?? string.Empty;
+ }
+ public string NodeName { get; }
+}
diff --git a/src/Microsoft.PowerPlatform.PowerApps.Persistence/Models/Label.cs b/src/Microsoft.PowerPlatform.PowerApps.Persistence/Models/Label.cs
new file mode 100644
index 00000000..f85f8f0f
--- /dev/null
+++ b/src/Microsoft.PowerPlatform.PowerApps.Persistence/Models/Label.cs
@@ -0,0 +1,14 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+namespace Microsoft.PowerPlatform.PowerApps.Persistence.Models;
+
+[FirstClass(Template)]
+internal record Label : Control
+{
+ internal const string Template = "Label";
+ public Label()
+ {
+ ControlUri = BuiltInTemplatesUris.Label;
+ }
+}
diff --git a/src/Microsoft.PowerPlatform.PowerApps.Persistence/Models/Screen.cs b/src/Microsoft.PowerPlatform.PowerApps.Persistence/Models/Screen.cs
new file mode 100644
index 00000000..1d23f07d
--- /dev/null
+++ b/src/Microsoft.PowerPlatform.PowerApps.Persistence/Models/Screen.cs
@@ -0,0 +1,14 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+namespace Microsoft.PowerPlatform.PowerApps.Persistence.Models;
+
+[FirstClass(Template)]
+internal record Screen : Control
+{
+ internal const string Template = "Screen";
+ public Screen()
+ {
+ ControlUri = BuiltInTemplatesUris.Screen;
+ }
+}
diff --git a/src/PASopa.sln b/src/PASopa.sln
index 72221e76..3b25ae46 100644
--- a/src/PASopa.sln
+++ b/src/PASopa.sln
@@ -16,6 +16,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
..\.editorconfig = ..\.editorconfig
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.PowerPlatform.PowerApps.Persistence", "Microsoft.PowerPlatform.PowerApps.Persistence\Microsoft.PowerPlatform.PowerApps.Persistence.csproj", "{906B4EA5-F287-4D0E-A511-0D89B2DD9C14}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -34,6 +36,10 @@ Global
{8AD94CC0-7330-4880-A8E0-177B37CDB27B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8AD94CC0-7330-4880-A8E0-177B37CDB27B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8AD94CC0-7330-4880-A8E0-177B37CDB27B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {906B4EA5-F287-4D0E-A511-0D89B2DD9C14}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {906B4EA5-F287-4D0E-A511-0D89B2DD9C14}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {906B4EA5-F287-4D0E-A511-0D89B2DD9C14}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {906B4EA5-F287-4D0E-A511-0D89B2DD9C14}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE