diff --git a/NArchitecture.sln b/NArchitecture.sln
index 06e28076..b7a691cc 100644
--- a/NArchitecture.sln
+++ b/NArchitecture.sln
@@ -32,6 +32,20 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
README.md = README.md
EndProjectSection
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "corePackages", "corePackages", "{F7AAFAB2-1A86-4672-A160-A1F8277CDE3E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.Application", "src\corePackages\Core.Application\Core.Application.csproj", "{41A6CDA6-B909-4BC4-8466-51EE8CC03018}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.CrossCuttingConcers", "src\corePackages\Core.CrossCuttingConcers\Core.CrossCuttingConcers.csproj", "{ED379FAF-0F5A-42E4-AA07-715F8280E2FC}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.ElasticSearch", "src\corePackages\Core.ElasticSearch\Core.ElasticSearch.csproj", "{4A5607F1-D28E-4E2D-ADCE-F94A3B3BD96D}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.Mailing", "src\corePackages\Core.Mailing\Core.Mailing.csproj", "{7CE04CAC-8306-4E5C-9524-67C3F3C66E88}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.Persistence", "src\corePackages\Core.Persistence\Core.Persistence.csproj", "{1FE0F616-CEBE-4589-AE2E-DD14D0751CC2}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.Security", "src\corePackages\Core.Security\Core.Security.csproj", "{D20AC6FA-3133-40B1-B4B2-630F6D08ECF9}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -62,6 +76,30 @@ Global
{A06975C0-631D-4E8B-8106-D1C8E3A05D19}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A06975C0-631D-4E8B-8106-D1C8E3A05D19}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A06975C0-631D-4E8B-8106-D1C8E3A05D19}.Release|Any CPU.Build.0 = Release|Any CPU
+ {41A6CDA6-B909-4BC4-8466-51EE8CC03018}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {41A6CDA6-B909-4BC4-8466-51EE8CC03018}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {41A6CDA6-B909-4BC4-8466-51EE8CC03018}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {41A6CDA6-B909-4BC4-8466-51EE8CC03018}.Release|Any CPU.Build.0 = Release|Any CPU
+ {ED379FAF-0F5A-42E4-AA07-715F8280E2FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {ED379FAF-0F5A-42E4-AA07-715F8280E2FC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {ED379FAF-0F5A-42E4-AA07-715F8280E2FC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {ED379FAF-0F5A-42E4-AA07-715F8280E2FC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4A5607F1-D28E-4E2D-ADCE-F94A3B3BD96D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4A5607F1-D28E-4E2D-ADCE-F94A3B3BD96D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4A5607F1-D28E-4E2D-ADCE-F94A3B3BD96D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4A5607F1-D28E-4E2D-ADCE-F94A3B3BD96D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7CE04CAC-8306-4E5C-9524-67C3F3C66E88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7CE04CAC-8306-4E5C-9524-67C3F3C66E88}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7CE04CAC-8306-4E5C-9524-67C3F3C66E88}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7CE04CAC-8306-4E5C-9524-67C3F3C66E88}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1FE0F616-CEBE-4589-AE2E-DD14D0751CC2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1FE0F616-CEBE-4589-AE2E-DD14D0751CC2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1FE0F616-CEBE-4589-AE2E-DD14D0751CC2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1FE0F616-CEBE-4589-AE2E-DD14D0751CC2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D20AC6FA-3133-40B1-B4B2-630F6D08ECF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D20AC6FA-3133-40B1-B4B2-630F6D08ECF9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D20AC6FA-3133-40B1-B4B2-630F6D08ECF9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D20AC6FA-3133-40B1-B4B2-630F6D08ECF9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -74,6 +112,13 @@ Global
{5A5C5789-75A7-4773-AB44-13721819502A} = {F4CFDEC8-18F0-4A9C-A16B-3045B325FA69}
{8696C5CF-50A9-41A3-848F-414D39A8FB21} = {F4CFDEC8-18F0-4A9C-A16B-3045B325FA69}
{A06975C0-631D-4E8B-8106-D1C8E3A05D19} = {07E15C51-014F-4C05-B709-C273D2D4E78C}
+ {F7AAFAB2-1A86-4672-A160-A1F8277CDE3E} = {BC7CA20F-741B-4757-8833-EA38E62FC786}
+ {41A6CDA6-B909-4BC4-8466-51EE8CC03018} = {F7AAFAB2-1A86-4672-A160-A1F8277CDE3E}
+ {ED379FAF-0F5A-42E4-AA07-715F8280E2FC} = {F7AAFAB2-1A86-4672-A160-A1F8277CDE3E}
+ {4A5607F1-D28E-4E2D-ADCE-F94A3B3BD96D} = {F7AAFAB2-1A86-4672-A160-A1F8277CDE3E}
+ {7CE04CAC-8306-4E5C-9524-67C3F3C66E88} = {F7AAFAB2-1A86-4672-A160-A1F8277CDE3E}
+ {1FE0F616-CEBE-4589-AE2E-DD14D0751CC2} = {F7AAFAB2-1A86-4672-A160-A1F8277CDE3E}
+ {D20AC6FA-3133-40B1-B4B2-630F6D08ECF9} = {F7AAFAB2-1A86-4672-A160-A1F8277CDE3E}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9208CFCE-156A-49CD-9E43-32CE67AAB957}
diff --git a/src/corePackages/Core.Application/Core.Application.csproj b/src/corePackages/Core.Application/Core.Application.csproj
new file mode 100644
index 00000000..8d089573
--- /dev/null
+++ b/src/corePackages/Core.Application/Core.Application.csproj
@@ -0,0 +1,26 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/corePackages/Core.Application/Pipelines/Authorization/AuthorizationBehavior.cs b/src/corePackages/Core.Application/Pipelines/Authorization/AuthorizationBehavior.cs
new file mode 100644
index 00000000..913624d0
--- /dev/null
+++ b/src/corePackages/Core.Application/Pipelines/Authorization/AuthorizationBehavior.cs
@@ -0,0 +1,33 @@
+using Core.CrossCuttingConcerns.Exceptions;
+using Core.Security.Extensions;
+using MediatR;
+using Microsoft.AspNetCore.Http;
+using Microsoft.IdentityModel.Tokens;
+
+namespace Core.Application.Pipelines.Authorization;
+
+public class AuthorizationBehavior : IPipelineBehavior
+ where TRequest : IRequest, ISecuredRequest
+{
+ private readonly IHttpContextAccessor _httpContextAccessor;
+
+ public AuthorizationBehavior(IHttpContextAccessor httpContextAccessor)
+ {
+ _httpContextAccessor = httpContextAccessor;
+ }
+
+ public async Task Handle(TRequest request, CancellationToken cancellationToken,
+ RequestHandlerDelegate next)
+ {
+ List? roleClaims = _httpContextAccessor.HttpContext.User.ClaimRoles();
+
+ if (roleClaims == null) throw new AuthorizationException("Claims not found.");
+
+ bool isNotMatchedARoleClaimWithRequestRoles =
+ roleClaims.FirstOrDefault(roleClaim => request.Roles.Any(role => role == roleClaim)).IsNullOrEmpty();
+ if (isNotMatchedARoleClaimWithRequestRoles) throw new AuthorizationException("You are not authorized.");
+
+ TResponse response = await next();
+ return response;
+ }
+}
\ No newline at end of file
diff --git a/src/corePackages/Core.Application/Pipelines/Authorization/ISecuredRequest.cs b/src/corePackages/Core.Application/Pipelines/Authorization/ISecuredRequest.cs
new file mode 100644
index 00000000..3914cba8
--- /dev/null
+++ b/src/corePackages/Core.Application/Pipelines/Authorization/ISecuredRequest.cs
@@ -0,0 +1,6 @@
+namespace Core.Application.Pipelines.Authorization;
+
+public interface ISecuredRequest
+{
+ public string[] Roles { get; }
+}
\ No newline at end of file
diff --git a/src/corePackages/Core.Application/Pipelines/Caching/CacheRemovingBehavior.cs b/src/corePackages/Core.Application/Pipelines/Caching/CacheRemovingBehavior.cs
new file mode 100644
index 00000000..7040d80c
--- /dev/null
+++ b/src/corePackages/Core.Application/Pipelines/Caching/CacheRemovingBehavior.cs
@@ -0,0 +1,38 @@
+using MediatR;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Logging;
+
+namespace Core.Application.Pipelines.Caching;
+
+public class CacheRemovingBehavior : IPipelineBehavior
+ where TRequest : IRequest, ICacheRemoverRequest
+{
+ private readonly IDistributedCache _cache;
+ private readonly ILogger> _logger;
+
+ public CacheRemovingBehavior(IDistributedCache cache, ILogger> logger
+ )
+ {
+ _cache = cache;
+ _logger = logger;
+ }
+
+ public async Task Handle(TRequest request, CancellationToken cancellationToken,
+ RequestHandlerDelegate next)
+ {
+ TResponse response;
+ if (request.BypassCache) return await next();
+
+ async Task GetResponseAndRemoveCache()
+ {
+ response = await next();
+ await _cache.RemoveAsync(request.CacheKey, cancellationToken);
+ return response;
+ }
+
+ response = await GetResponseAndRemoveCache();
+ _logger.LogInformation($"Removed Cache -> {request.CacheKey}");
+
+ return response;
+ }
+}
\ No newline at end of file
diff --git a/src/corePackages/Core.Application/Pipelines/Caching/CacheSettings.cs b/src/corePackages/Core.Application/Pipelines/Caching/CacheSettings.cs
new file mode 100644
index 00000000..6d7d8d4a
--- /dev/null
+++ b/src/corePackages/Core.Application/Pipelines/Caching/CacheSettings.cs
@@ -0,0 +1,6 @@
+namespace Core.Application.Pipelines.Caching;
+
+public class CacheSettings
+{
+ public int SlidingExpiration { get; set; }
+}
\ No newline at end of file
diff --git a/src/corePackages/Core.Application/Pipelines/Caching/CachingBehavior.cs b/src/corePackages/Core.Application/Pipelines/Caching/CachingBehavior.cs
new file mode 100644
index 00000000..ac5b7df9
--- /dev/null
+++ b/src/corePackages/Core.Application/Pipelines/Caching/CachingBehavior.cs
@@ -0,0 +1,57 @@
+using MediatR;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
+using Newtonsoft.Json;
+using System.Text;
+
+namespace Core.Application.Pipelines.Caching;
+
+public class CachingBehavior : IPipelineBehavior
+ where TRequest : IRequest, ICachableRequest
+{
+ private readonly IDistributedCache _cache;
+ private readonly ILogger> _logger;
+
+ private readonly CacheSettings _cacheSettings;
+
+ public CachingBehavior(IDistributedCache cache, ILogger> logger,
+ IConfiguration configuration)
+ {
+ _cache = cache;
+ _logger = logger;
+ _cacheSettings = configuration.GetSection("CacheSettings").Get();
+ }
+
+ public async Task Handle(TRequest request, CancellationToken cancellationToken,
+ RequestHandlerDelegate next)
+ {
+ TResponse response;
+ if (request.BypassCache) return await next();
+
+ async Task GetResponseAndAddToCache()
+ {
+ response = await next();
+ TimeSpan? slidingExpiration =
+ request.SlidingExpiration ?? TimeSpan.FromDays(_cacheSettings.SlidingExpiration);
+ DistributedCacheEntryOptions cacheOptions = new() { SlidingExpiration = slidingExpiration };
+ byte[] serializeData = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(response));
+ await _cache.SetAsync(request.CacheKey, serializeData, cacheOptions, cancellationToken);
+ return response;
+ }
+
+ byte[]? cachedResponse = await _cache.GetAsync(request.CacheKey, cancellationToken);
+ if (cachedResponse != null)
+ {
+ response = JsonConvert.DeserializeObject(Encoding.Default.GetString(cachedResponse));
+ _logger.LogInformation($"Fetched from Cache -> {request.CacheKey}");
+ }
+ else
+ {
+ response = await GetResponseAndAddToCache();
+ _logger.LogInformation($"Added to Cache -> {request.CacheKey}");
+ }
+
+ return response;
+ }
+}
\ No newline at end of file
diff --git a/src/corePackages/Core.Application/Pipelines/Caching/ICachableRequest.cs b/src/corePackages/Core.Application/Pipelines/Caching/ICachableRequest.cs
new file mode 100644
index 00000000..a2b79db6
--- /dev/null
+++ b/src/corePackages/Core.Application/Pipelines/Caching/ICachableRequest.cs
@@ -0,0 +1,8 @@
+namespace Core.Application.Pipelines.Caching;
+
+public interface ICachableRequest
+{
+ bool BypassCache { get; }
+ string CacheKey { get; }
+ TimeSpan? SlidingExpiration { get; }
+}
\ No newline at end of file
diff --git a/src/corePackages/Core.Application/Pipelines/Caching/ICacheRemoverRequest.cs b/src/corePackages/Core.Application/Pipelines/Caching/ICacheRemoverRequest.cs
new file mode 100644
index 00000000..0f1f58f6
--- /dev/null
+++ b/src/corePackages/Core.Application/Pipelines/Caching/ICacheRemoverRequest.cs
@@ -0,0 +1,7 @@
+namespace Core.Application.Pipelines.Caching;
+
+public interface ICacheRemoverRequest
+{
+ bool BypassCache { get; }
+ string CacheKey { get; }
+}
\ No newline at end of file
diff --git a/src/corePackages/Core.Application/Pipelines/Logging/ILoggableRequest.cs b/src/corePackages/Core.Application/Pipelines/Logging/ILoggableRequest.cs
new file mode 100644
index 00000000..3a295fb2
--- /dev/null
+++ b/src/corePackages/Core.Application/Pipelines/Logging/ILoggableRequest.cs
@@ -0,0 +1,5 @@
+namespace Core.Application.Pipelines.Logging;
+
+public interface ILoggableRequest
+{
+}
\ No newline at end of file
diff --git a/src/corePackages/Core.Application/Pipelines/Logging/LoggingBehavior.cs b/src/corePackages/Core.Application/Pipelines/Logging/LoggingBehavior.cs
new file mode 100644
index 00000000..27d0fd28
--- /dev/null
+++ b/src/corePackages/Core.Application/Pipelines/Logging/LoggingBehavior.cs
@@ -0,0 +1,46 @@
+using Core.CrossCuttingConcerns.Logging;
+using Core.CrossCuttingConcerns.Logging.Serilog;
+using MediatR;
+using Microsoft.AspNetCore.Http;
+using Newtonsoft.Json;
+
+namespace Core.Application.Pipelines.Logging;
+
+public class LoggingBehavior : IPipelineBehavior
+ where TRequest : IRequest, ILoggableRequest
+{
+ private readonly LoggerServiceBase _loggerServiceBase;
+ private readonly IHttpContextAccessor _httpContextAccessor;
+
+
+ public LoggingBehavior(LoggerServiceBase loggerServiceBase, IHttpContextAccessor httpContextAccessor)
+ {
+ _loggerServiceBase = loggerServiceBase;
+ _httpContextAccessor = httpContextAccessor;
+ }
+
+ public Task Handle(TRequest request, CancellationToken cancellationToken,
+ RequestHandlerDelegate next)
+ {
+ List logParameters = new();
+ logParameters.Add(new LogParameter
+ {
+ Type = request.GetType().Name,
+ Value = request
+ });
+
+ LogDetail logDetail = new()
+ {
+ MethodName = next.Method.Name,
+ Parameters = logParameters,
+ User = _httpContextAccessor.HttpContext == null ||
+ _httpContextAccessor.HttpContext.User.Identity.Name == null
+ ? "?"
+ : _httpContextAccessor.HttpContext.User.Identity.Name
+ };
+
+ _loggerServiceBase.Info(JsonConvert.SerializeObject(logDetail));
+
+ return next();
+ }
+}
\ No newline at end of file
diff --git a/src/corePackages/Core.Application/Pipelines/Validation/RequestValidationBehavior.cs b/src/corePackages/Core.Application/Pipelines/Validation/RequestValidationBehavior.cs
new file mode 100644
index 00000000..6f988390
--- /dev/null
+++ b/src/corePackages/Core.Application/Pipelines/Validation/RequestValidationBehavior.cs
@@ -0,0 +1,29 @@
+using FluentValidation;
+using FluentValidation.Results;
+using MediatR;
+
+namespace Core.Application.Pipelines.Validation;
+
+public class RequestValidationBehavior : IPipelineBehavior
+ where TRequest : IRequest
+{
+ private readonly IEnumerable> _validators;
+
+ public RequestValidationBehavior(IEnumerable> validators)
+ {
+ _validators = validators;
+ }
+
+ public Task Handle(TRequest request, CancellationToken cancellationToken,
+ RequestHandlerDelegate next)
+ {
+ ValidationContext