diff --git a/ADotNet/ADotNet.csproj b/ADotNet/ADotNet.csproj
index 10d168a..a3b1dc5 100644
--- a/ADotNet/ADotNet.csproj
+++ b/ADotNet/ADotNet.csproj
@@ -42,6 +42,7 @@
+
diff --git a/ADotNet/Models/Pipelines/Releases/GithubPipelines/Exceptions/NullReleasePathException.cs b/ADotNet/Models/Pipelines/Releases/GithubPipelines/Exceptions/NullReleasePathException.cs
new file mode 100644
index 0000000..f3e359c
--- /dev/null
+++ b/ADotNet/Models/Pipelines/Releases/GithubPipelines/Exceptions/NullReleasePathException.cs
@@ -0,0 +1,22 @@
+// ---------------------------------------------------------------
+// Copyright (c) Hassan Habib All rights reserved.
+// Licensed under the MIT License.
+// See License.txt in the project root for license information.
+// ---------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Xeptions;
+
+namespace ADotNet.Models.Pipelines.Releases.GithubPipelines.Exceptions
+{
+ public class NullReleasePathException : Xeption
+ {
+ public NullReleasePathException()
+ : base(message: "Release path is null.")
+ { }
+ }
+}
diff --git a/ADotNet/Models/Pipelines/Releases/GithubPipelines/Exceptions/NullReleasePipelineException.cs b/ADotNet/Models/Pipelines/Releases/GithubPipelines/Exceptions/NullReleasePipelineException.cs
new file mode 100644
index 0000000..dba76f3
--- /dev/null
+++ b/ADotNet/Models/Pipelines/Releases/GithubPipelines/Exceptions/NullReleasePipelineException.cs
@@ -0,0 +1,17 @@
+// ---------------------------------------------------------------
+// Copyright (c) Hassan Habib All rights reserved.
+// Licensed under the MIT License.
+// See License.txt in the project root for license information.
+// ---------------------------------------------------------------
+
+using Xeptions;
+
+namespace ADotNet.Models.Pipelines.Releases.GithubPipelines.Exceptions
+{
+ public class NullReleasePipelineException : Xeption
+ {
+ public NullReleasePipelineException()
+ : base(message: "Release pipeline is null.")
+ { }
+ }
+}
diff --git a/ADotNet/Models/Pipelines/Releases/GithubPipelines/Exceptions/ReleaseValidationException.cs b/ADotNet/Models/Pipelines/Releases/GithubPipelines/Exceptions/ReleaseValidationException.cs
new file mode 100644
index 0000000..d5660de
--- /dev/null
+++ b/ADotNet/Models/Pipelines/Releases/GithubPipelines/Exceptions/ReleaseValidationException.cs
@@ -0,0 +1,23 @@
+// ---------------------------------------------------------------
+// Copyright (c) Hassan Habib All rights reserved.
+// Licensed under the MIT License.
+// See License.txt in the project root for license information.
+// ---------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Xeptions;
+
+namespace ADotNet.Models.Pipelines.Releases.GithubPipelines.Exceptions
+{
+ public class ReleaseValidationException : Xeption
+ {
+ public ReleaseValidationException(Xeption innerException)
+ : base(message: "Release validation error occurred, fix the errors and try again.",
+ innerException)
+ { }
+ }
+}
diff --git a/ADotNet/Models/Pipelines/Releases/GithubPipelines/GithubReleasePipeline.cs b/ADotNet/Models/Pipelines/Releases/GithubPipelines/GithubReleasePipeline.cs
new file mode 100644
index 0000000..8d323b5
--- /dev/null
+++ b/ADotNet/Models/Pipelines/Releases/GithubPipelines/GithubReleasePipeline.cs
@@ -0,0 +1,12 @@
+// ---------------------------------------------------------------
+// Copyright (c) Hassan Habib All rights reserved.
+// Licensed under the MIT License.
+// See License.txt in the project root for license information.
+// ---------------------------------------------------------------
+
+namespace ADotNet.Models.Pipelines.Releases.GithubPipelines
+{
+ public class GithubReleasePipeline
+ {
+ }
+}
diff --git a/ADotNet/Services/Releases/IReleaseService.cs b/ADotNet/Services/Releases/IReleaseService.cs
new file mode 100644
index 0000000..9c89411
--- /dev/null
+++ b/ADotNet/Services/Releases/IReleaseService.cs
@@ -0,0 +1,13 @@
+// ---------------------------------------------------------------
+// Copyright (c) Hassan Habib All rights reserved.
+// Licensed under the MIT License.
+// See License.txt in the project root for license information.
+// ---------------------------------------------------------------
+
+namespace ADotNet.Services.Releases
+{
+ public interface IReleaseService
+ {
+ void SerializeWriteToFile(string path, object releasePipeline);
+ }
+}
diff --git a/ADotNet/Services/Releases/ReleaseService.Exceptions.cs b/ADotNet/Services/Releases/ReleaseService.Exceptions.cs
new file mode 100644
index 0000000..ea5c0d9
--- /dev/null
+++ b/ADotNet/Services/Releases/ReleaseService.Exceptions.cs
@@ -0,0 +1,31 @@
+// ---------------------------------------------------------------
+// Copyright (c) Hassan Habib All rights reserved.
+// Licensed under the MIT License.
+// See License.txt in the project root for license information.
+// ---------------------------------------------------------------
+
+using ADotNet.Models.Pipelines.Releases.GithubPipelines.Exceptions;
+
+namespace ADotNet.Services.Releases
+{
+ public partial class ReleaseService
+ {
+ private delegate void ReturningNothingFunction();
+
+ private void TryCatch(ReturningNothingFunction returningNothingFunction)
+ {
+ try
+ {
+ returningNothingFunction();
+ }
+ catch (NullReleasePathException nullReleasePathException)
+ {
+ throw new ReleaseValidationException(nullReleasePathException);
+ }
+ catch (NullReleasePipelineException nullReleasePipelineException)
+ {
+ throw new ReleaseValidationException(nullReleasePipelineException);
+ }
+ }
+ }
+}
diff --git a/ADotNet/Services/Releases/ReleaseService.Validations.cs b/ADotNet/Services/Releases/ReleaseService.Validations.cs
new file mode 100644
index 0000000..89d5fed
--- /dev/null
+++ b/ADotNet/Services/Releases/ReleaseService.Validations.cs
@@ -0,0 +1,26 @@
+// ---------------------------------------------------------------
+// Copyright (c) Hassan Habib All rights reserved.
+// Licensed under the MIT License.
+// See License.txt in the project root for license information.
+// ---------------------------------------------------------------
+
+using ADotNet.Models.Pipelines.Releases.GithubPipelines;
+using ADotNet.Models.Pipelines.Releases.GithubPipelines.Exceptions;
+
+namespace ADotNet.Services.Releases
+{
+ public partial class ReleaseService
+ {
+ private static void ValidateInputs(string path, object pipeline)
+ {
+ switch(path, pipeline)
+ {
+ case (null, _):
+ throw new NullReleasePathException();
+
+ case (_, null):
+ throw new NullReleasePipelineException();
+ }
+ }
+ }
+}
diff --git a/ADotNet/Services/Releases/ReleaseService.cs b/ADotNet/Services/Releases/ReleaseService.cs
new file mode 100644
index 0000000..a4ebd9a
--- /dev/null
+++ b/ADotNet/Services/Releases/ReleaseService.cs
@@ -0,0 +1,39 @@
+// ---------------------------------------------------------------
+// Copyright (c) Hassan Habib All rights reserved.
+// Licensed under the MIT License.
+// See License.txt in the project root for license information.
+// ---------------------------------------------------------------
+
+using System;
+using ADotNet.Brokers.IOs;
+using ADotNet.Brokers.Serializers;
+
+namespace ADotNet.Services.Releases
+{
+ public partial class ReleaseService : IReleaseService
+ {
+ private readonly IYamlBroker yamlBroker;
+ private readonly IFilesBroker filesBroker;
+
+ public ReleaseService(
+ IYamlBroker yamlBroker,
+ IFilesBroker filesBroker)
+ {
+ this.yamlBroker = yamlBroker;
+ this.filesBroker = filesBroker;
+ }
+
+ public void SerializeWriteToFile(string path, object releasePipeline) =>
+ TryCatch(() =>
+ {
+ ValidateInputs(path, releasePipeline);
+
+ string serializedPipeline =
+ this.yamlBroker.SerializeToYaml(releasePipeline);
+
+ this.filesBroker.WriteToFile(
+ path,
+ data: serializedPipeline);
+ });
+ }
+}
diff --git a/AdoNet.Tests.Unit/AdoNet.Tests.Unit.csproj b/AdoNet.Tests.Unit/AdoNet.Tests.Unit.csproj
index e6c1bd6..e0c403b 100644
--- a/AdoNet.Tests.Unit/AdoNet.Tests.Unit.csproj
+++ b/AdoNet.Tests.Unit/AdoNet.Tests.Unit.csproj
@@ -8,7 +8,7 @@
-
+
diff --git a/AdoNet.Tests.Unit/Services/Foundations/Releases/ReleaseServiceTests.Logic.cs b/AdoNet.Tests.Unit/Services/Foundations/Releases/ReleaseServiceTests.Logic.cs
new file mode 100644
index 0000000..528d9fd
--- /dev/null
+++ b/AdoNet.Tests.Unit/Services/Foundations/Releases/ReleaseServiceTests.Logic.cs
@@ -0,0 +1,53 @@
+// ---------------------------------------------------------------
+// Copyright (c) Hassan Habib All rights reserved.
+// Licensed under the MIT License.
+// See License.txt in the project root for license information.
+// ---------------------------------------------------------------
+
+using ADotNet.Models.Pipelines.Releases.GithubPipelines;
+using Moq;
+using Xunit;
+
+namespace AdoNet.Tests.Unit.Services.Foundations.Releases
+{
+ public partial class ReleaseServiceTests
+ {
+ [Fact]
+ public void ShouldSerializeWriteReleasePipeline()
+ {
+ // given
+ string randomSerializedPipeline = CreateRandomString();
+ string serializedGithubPipeline = randomSerializedPipeline;
+ string randomPath = CreateRandomString();
+ string inputPath = randomPath;
+
+ GithubReleasePipeline randomGithubReleasePipeline =
+ CreateRandomReleasePipeline();
+
+ GithubReleasePipeline inputGithubReleasePipeline =
+ randomGithubReleasePipeline;
+
+ this.yamlBrokerMock.Setup(broker =>
+ broker.SerializeToYaml(inputGithubReleasePipeline))
+ .Returns(serializedGithubPipeline);
+
+ // when
+ this.releaseService.SerializeWriteToFile(
+ inputPath,
+ inputGithubReleasePipeline);
+
+ // then
+
+ this.yamlBrokerMock.Verify(broker =>
+ broker.SerializeToYaml(inputGithubReleasePipeline),
+ Times.Once);
+
+ this.filesBrokerMock.Verify(broker =>
+ broker.WriteToFile(inputPath, serializedGithubPipeline),
+ Times.Once);
+
+ this.yamlBrokerMock.VerifyNoOtherCalls();
+ this.filesBrokerMock.VerifyNoOtherCalls();
+ }
+ }
+}
diff --git a/AdoNet.Tests.Unit/Services/Foundations/Releases/ReleaseServiceTests.Validations.cs b/AdoNet.Tests.Unit/Services/Foundations/Releases/ReleaseServiceTests.Validations.cs
new file mode 100644
index 0000000..bbda7b9
--- /dev/null
+++ b/AdoNet.Tests.Unit/Services/Foundations/Releases/ReleaseServiceTests.Validations.cs
@@ -0,0 +1,87 @@
+// ---------------------------------------------------------------
+// Copyright (c) Hassan Habib All rights reserved.
+// Licensed under the MIT License.
+// See License.txt in the project root for license information.
+// ---------------------------------------------------------------
+
+using System;
+using ADotNet.Models.Pipelines.Releases.GithubPipelines;
+using ADotNet.Models.Pipelines.Releases.GithubPipelines.Exceptions;
+using Moq;
+using Xunit;
+
+namespace AdoNet.Tests.Unit.Services.Foundations.Releases
+{
+ public partial class ReleaseServiceTests
+ {
+ [Fact]
+ public void ShouldThrowValidationExceptionOnSerializeWriteIfPathIsNull()
+ {
+ // given
+ string invalidPath = null;
+
+ GithubReleasePipeline someReleasePipeline =
+ CreateRandomReleasePipeline();
+
+ var nullReleasePathException =
+ new NullReleasePathException();
+
+ var expectedReleaseValidationException =
+ new ReleaseValidationException(
+ nullReleasePathException);
+
+ // when
+ Action serializeWriteAction = () =>
+ this.releaseService.SerializeWriteToFile(invalidPath, someReleasePipeline);
+
+ // then
+ Assert.Throws(serializeWriteAction);
+
+ this.yamlBrokerMock.Verify(broker =>
+ broker.SerializeToYaml(It.IsAny