diff --git a/Connected.ServiceModel/Connected.ServiceModel.csproj b/Connected.ServiceModel/Connected.ServiceModel.csproj
new file mode 100644
index 0000000..ca8b131
--- /dev/null
+++ b/Connected.ServiceModel/Connected.ServiceModel.csproj
@@ -0,0 +1,19 @@
+
+
+
+ net7.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Connected.ServiceModel/Data/ITableEntity.cs b/Connected.ServiceModel/Data/ITableEntity.cs
new file mode 100644
index 0000000..2f0a854
--- /dev/null
+++ b/Connected.ServiceModel/Data/ITableEntity.cs
@@ -0,0 +1,10 @@
+using Connected.Entities.Consistency;
+
+namespace Connected.ServiceModel.Data;
+
+public interface ITableEntity : IConsistentEntity
+ where TPrimaryKey : notnull
+ where TPartitionKey : notnull
+{
+ TPartitionKey PartitionKey { get; init; }
+}
diff --git a/Connected.ServiceModel/Data/ITableService.cs b/Connected.ServiceModel/Data/ITableService.cs
new file mode 100644
index 0000000..19bbe9d
--- /dev/null
+++ b/Connected.ServiceModel/Data/ITableService.cs
@@ -0,0 +1,12 @@
+using System.Collections.Immutable;
+
+namespace Connected.ServiceModel.Data;
+
+public interface ITableService
+{
+ Task> Query(string commandText);
+
+ Task Select(string commandText);
+
+ Task Execute(string commandText);
+}
diff --git a/Connected.ServiceModel/Data/TableEntity.cs b/Connected.ServiceModel/Data/TableEntity.cs
new file mode 100644
index 0000000..b2dce90
--- /dev/null
+++ b/Connected.ServiceModel/Data/TableEntity.cs
@@ -0,0 +1,12 @@
+using Connected.Annotations;
+using Connected.Entities.Consistency;
+
+namespace Connected.ServiceModel.Data;
+
+public abstract record TableEntity : ConsistentEntity, ITableEntity
+ where TPrimaryKey : notnull
+ where TPartitionKey : notnull
+{
+ [Ordinal(-1)]
+ public TPartitionKey PartitionKey { get; init; } = default!;
+}
diff --git a/Connected.ServiceModel/Search/ISearchEntity.cs b/Connected.ServiceModel/Search/ISearchEntity.cs
new file mode 100644
index 0000000..3eff317
--- /dev/null
+++ b/Connected.ServiceModel/Search/ISearchEntity.cs
@@ -0,0 +1,11 @@
+using Connected.Entities.Consistency;
+
+namespace Connected.ServiceModel.Search;
+public interface ISearchEntity : IConsistentEntity
+ where TPrimaryKey : notnull
+{
+ int Author { get; init; }
+ DateTimeOffset? Created { get; init; }
+ int Language { get; init; }
+ string? Tags { get; init; }
+}
diff --git a/Connected.ServiceModel/Search/ISearchService.cs b/Connected.ServiceModel/Search/ISearchService.cs
new file mode 100644
index 0000000..a63211d
--- /dev/null
+++ b/Connected.ServiceModel/Search/ISearchService.cs
@@ -0,0 +1,15 @@
+using System.Collections.Immutable;
+using Connected.Entities;
+
+namespace Connected.ServiceModel.Search;
+
+public interface ISearchService
+{
+ Task> Query(string catalog, string criteria);
+
+ Task Update(string catalog, TEntity entity)
+ where TEntity : IEntity;
+
+ Task Update(string catalog, IEnumerable entity)
+ where TEntity : IEntity;
+}
diff --git a/Connected.ServiceModel/Search/SearchEntity.cs b/Connected.ServiceModel/Search/SearchEntity.cs
new file mode 100644
index 0000000..a6d3dc3
--- /dev/null
+++ b/Connected.ServiceModel/Search/SearchEntity.cs
@@ -0,0 +1,20 @@
+using Connected.Annotations;
+using Connected.Entities.Annotations;
+using Connected.Entities.Consistency;
+
+namespace Connected.ServiceModel.Search;
+public record SearchEntity : ConsistentEntity, ISearchEntity
+ where TPrimaryKey : notnull
+{
+ [Ordinal(0)]
+ public int Author { get; init; }
+
+ [Ordinal(1)]
+ public DateTimeOffset? Created { get; init; }
+
+ [Ordinal(2)]
+ public int Language { get; init; }
+
+ [Ordinal(3), Nullable]
+ public string? Tags { get; init; }
+}
diff --git a/Connected.ServiceModel/Search/SearchExtensions.cs b/Connected.ServiceModel/Search/SearchExtensions.cs
new file mode 100644
index 0000000..184afd8
--- /dev/null
+++ b/Connected.ServiceModel/Search/SearchExtensions.cs
@@ -0,0 +1,9 @@
+namespace Connected.ServiceModel.Search;
+public static class SearchExtensions
+{
+ public static IEnumerable WithPaging(this IEnumerable entities, SearchArgs args)
+ {
+ return entities.Skip((args.Paging.Index - 1) * args.Paging.Size)
+ .Take(args.Paging.Size);
+ }
+}
diff --git a/Connected.ServiceModel/ServiceArgs.cs b/Connected.ServiceModel/ServiceArgs.cs
new file mode 100644
index 0000000..58a6d34
--- /dev/null
+++ b/Connected.ServiceModel/ServiceArgs.cs
@@ -0,0 +1,13 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace Connected.ServiceModel;
+
+public class ServiceArgs : Dto
+{
+ [Required]
+ [MaxLength(128)]
+ public string AccessToken { get; set; } = default!;
+
+ [Range(1, int.MaxValue)]
+ public int Subscription { get; set; }
+}
diff --git a/Connected.ServiceModel/ServiceModelStartup.cs b/Connected.ServiceModel/ServiceModelStartup.cs
new file mode 100644
index 0000000..bf82c68
--- /dev/null
+++ b/Connected.ServiceModel/ServiceModelStartup.cs
@@ -0,0 +1,9 @@
+using Connected.Annotations;
+
+[assembly: MicroService(MicroServiceType.Sys)]
+
+namespace Connected.ServiceModel;
+
+internal sealed class ServiceModelStartup : Startup
+{
+}
diff --git a/Connected.ServiceModel/Storage/IDirectory.cs b/Connected.ServiceModel/Storage/IDirectory.cs
new file mode 100644
index 0000000..1fd5b0f
--- /dev/null
+++ b/Connected.ServiceModel/Storage/IDirectory.cs
@@ -0,0 +1,7 @@
+namespace Connected.ServiceModel.Storage;
+
+public interface IDirectory
+{
+ string Name { get; }
+ DateTime Created { get; }
+}
diff --git a/Connected.ServiceModel/Storage/IFile.cs b/Connected.ServiceModel/Storage/IFile.cs
new file mode 100644
index 0000000..fc53c46
--- /dev/null
+++ b/Connected.ServiceModel/Storage/IFile.cs
@@ -0,0 +1,8 @@
+namespace Connected.ServiceModel.Storage;
+
+public interface IFile
+{
+ string Name { get; }
+ DateTime Created { get; }
+ long Size { get; }
+}
diff --git a/Connected.ServiceModel/Storage/IStorageService.cs b/Connected.ServiceModel/Storage/IStorageService.cs
new file mode 100644
index 0000000..c2a5c08
--- /dev/null
+++ b/Connected.ServiceModel/Storage/IStorageService.cs
@@ -0,0 +1,24 @@
+using System.Collections.Immutable;
+
+namespace Connected.ServiceModel.Storage;
+
+public interface IStorageService
+{
+ Task?> QueryFiles(DirectoryArgs args);
+
+ Task?> QueryDirectories(DirectoryArgs args);
+
+ Task SelectFile(FileArgs args);
+
+ Task UpdateFile(UpdateFileArgs args);
+
+ Task DeleteFile(DeleteFileArgs args);
+
+ Task MoveFile(MoveFileArgs args);
+
+ Task InsertDirectory(InsertDirectoryArgs args);
+
+ Task UpdateDirectory(UpdateDirectoryArgs args);
+
+ Task DeleteDirectory(DeleteDirectoryArgs args);
+}
diff --git a/Connected.ServiceModel/Storage/StorageArgs.cs b/Connected.ServiceModel/Storage/StorageArgs.cs
new file mode 100644
index 0000000..9a15f45
--- /dev/null
+++ b/Connected.ServiceModel/Storage/StorageArgs.cs
@@ -0,0 +1,59 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace Connected.ServiceModel.Storage;
+
+public class FileArgs : ServiceArgs
+{
+ [Required]
+ [MaxLength(1024)]
+ public string Directory { get; set; } = default!;
+
+ [MaxLength(256)]
+ [Required]
+ public string FileName { get; set; } = default!;
+}
+
+public sealed class DeleteFileArgs : FileArgs
+{
+
+}
+
+public sealed class UpdateFileArgs : FileArgs
+{
+ public byte[]? Content { get; set; }
+}
+
+public sealed class MoveFileArgs : FileArgs
+{
+ [Required]
+ [MaxLength(256)]
+ public string NewFileName { get; set; } = default!;
+
+ [Required]
+ [MaxLength(1024)]
+ public string NewDirectory { get; set; } = default!;
+}
+
+public class DirectoryArgs : ServiceArgs
+{
+ [Required]
+ [MaxLength(1024)]
+ public string Path { get; set; } = default!;
+}
+
+public sealed class InsertDirectoryArgs : DirectoryArgs
+{
+
+}
+
+public sealed class UpdateDirectoryArgs : DirectoryArgs
+{
+ [Required]
+ [MaxLength(1024)]
+ public string NewPath { get; set; } = default!;
+}
+
+public sealed class DeleteDirectoryArgs : DirectoryArgs
+{
+
+}
diff --git a/Framework.ServiceModel.sln b/Framework.ServiceModel.sln
new file mode 100644
index 0000000..092d795
--- /dev/null
+++ b/Framework.ServiceModel.sln
@@ -0,0 +1,50 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.4.33027.239
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Dependencies", "Dependencies", "{6EFC1819-8C82-49C6-B893-B84E8C218560}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Connected.ServiceModel", "Connected.ServiceModel\Connected.ServiceModel.csproj", "{EEAAA364-FCF2-4975-8321-A475778D684C}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Connected", "..\Connected\Connected\Connected.csproj", "{6F70D808-2CB4-4844-B72C-BE01E91C7861}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Connected.Runtime", "..\Framework\Connected.Runtime\Connected.Runtime.csproj", "{DDC87725-25E6-44C9-ABAC-C93FF4171F4B}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Connected.Entities", "..\Framework\Connected.Entities\Connected.Entities.csproj", "{99F5300F-E8A1-497A-835D-813890F4EA8A}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {EEAAA364-FCF2-4975-8321-A475778D684C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EEAAA364-FCF2-4975-8321-A475778D684C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EEAAA364-FCF2-4975-8321-A475778D684C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EEAAA364-FCF2-4975-8321-A475778D684C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6F70D808-2CB4-4844-B72C-BE01E91C7861}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6F70D808-2CB4-4844-B72C-BE01E91C7861}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6F70D808-2CB4-4844-B72C-BE01E91C7861}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6F70D808-2CB4-4844-B72C-BE01E91C7861}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DDC87725-25E6-44C9-ABAC-C93FF4171F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DDC87725-25E6-44C9-ABAC-C93FF4171F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DDC87725-25E6-44C9-ABAC-C93FF4171F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DDC87725-25E6-44C9-ABAC-C93FF4171F4B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {99F5300F-E8A1-497A-835D-813890F4EA8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {99F5300F-E8A1-497A-835D-813890F4EA8A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {99F5300F-E8A1-497A-835D-813890F4EA8A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {99F5300F-E8A1-497A-835D-813890F4EA8A}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {6F70D808-2CB4-4844-B72C-BE01E91C7861} = {6EFC1819-8C82-49C6-B893-B84E8C218560}
+ {DDC87725-25E6-44C9-ABAC-C93FF4171F4B} = {6EFC1819-8C82-49C6-B893-B84E8C218560}
+ {99F5300F-E8A1-497A-835D-813890F4EA8A} = {6EFC1819-8C82-49C6-B893-B84E8C218560}
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {1EC384B4-F27F-4F71-8BDC-16F7BA8689E9}
+ EndGlobalSection
+EndGlobal