Last active
February 15, 2021 22:09
-
-
Save fnbk/500d4d201e8d9de5898ead04826713ef to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# IOSP - Integration Operation Segregation Principle | |
# | |
Code is separated into functions of logic (operation) and non-logic (integration) - Ralf Westphal | |
IOSP calls for a clear separation: | |
* Operation: a method contains logic, transformations, control structures or API invocations | |
* Integration: a method only contains calls to other methods within its code base | |
If a method contains both logic and calls to other methods, the total behavior will be no longer clear. The instructions are blurred over a possibly very deep hierarchy. Furthermore this kind of method has a tendency to grow unlimited. | |
That explicit separation has many positive effects: | |
* Methods tend to stay short. | |
* More than 10, 20 or 30 lines of pure logic or exclusively method calls “feel strange”. | |
* As mixing is not allowed small methods will be extracted. | |
Operation Fuctions: | |
* Short logic methods are easy to test as they don’t have many dependencies. | |
* Short logic methods are relatively easy to understand. | |
* The methods name can drive the meaning. | |
Integration Functions: | |
* Short integration methods are very well understandable and reveal what happens at first glance. | |
* Correctness of integrations can be reviewed easily. Basically just the process order needs to be double-checked. Compiler and unit-tests of operations do the rest. | |
* Integrations can be nicely expanded by inserting additional method calls. Understandability stays. | |
# | |
# Integration | |
# | |
(does not contain any logic but exclusively calls other methods within its code base) | |
private Job CreateJob(Order order) | |
{ | |
var computer = GetComputer(order.ComputerName, order.CustomerId); | |
computer.CustomerId = GetCustomerId(order.CustomerId, computer.CustomerId); | |
var serverAddress = GetServerAddress(order.ComputerName, computer.CustomerId); | |
var deploymentType = GetDeploymentType(order.MaterialNumber, computer.CustomerId); | |
var job = NewJob(order, computer, serverAddress, deploymentType, jobActions); | |
return job; | |
} | |
# | |
# Operation | |
# | |
(contains exclusively logic, transformations, control structures or API invocations) | |
private (string returnValue, string errorNumber) ValidateOrderPosition(OrderPosition orderPosition) | |
{ | |
string returnValue = "Order accepted."; | |
string errorNumber = "99999"; | |
if (string.IsNullOrWhiteSpace(orderPosition.ComputerName)) | |
{ | |
returnValue = "Computer name must not be empty!"; | |
errorNumber = "2"; | |
} | |
else | |
{ | |
if (string.IsNullOrWhiteSpace(orderPosition.MaterialNumber)) | |
{ | |
returnValue = "Material number must not be empty!"; | |
errorNumber = "3"; | |
} | |
else | |
{ | |
if (orderPosition.MaterialNumber.IndexOf("-") != -1) | |
{ | |
returnValue = "Material number must not contain a dash!"; | |
errorNumber = "4"; | |
} | |
foreach (Site site in orderPosition.Sites) | |
{ | |
returnValue = $"{returnValue}-{site.Name}" | |
} | |
} | |
} | |
return (returnValue, errorNumber); | |
} | |
# | |
# Dirty (mixed) Example | |
# | |
(integrations and operations are mixed) | |
private async Job CreateJob(order order) | |
{ | |
Job job = new Job | |
{ | |
ActivationTime = order.ClientLocalStartTime, | |
ComputerName = order.ComputerName, | |
orderId = order.Id, | |
Status = "created" | |
}; | |
if (ValidateComputer(order, null, _serverSecret)) | |
{ | |
Computer computer = GetComputer(order.ComputerName, order.CustomerId); | |
string serverAddress = GetServerAddress(order.ComputerName, computer.CustomerId); | |
if (!string.IsNullOrWhiteSpace(serverAddress)) | |
{ | |
List<JobAction> actions = await CheckForLocalOsdReleaseAndCreateActionAsync(order, computer); | |
if (!actions.Any()) | |
{ | |
actions = await CheckForApplicationBundleAndCreateActionsAsync(order, computer); | |
} | |
if (!actions.Any()) | |
{ | |
actions = await CheckForCollectionAndCreateActionsAsync(order, computer); | |
} | |
if (!actions.Any()) | |
{ | |
actions = await CheckForApplicationAndCreateActionsAsync(order, computer); | |
} | |
foreach (JobAction action in actions) | |
{ | |
action.SiteServer = serverAddress; | |
} | |
job.JobActions = actions; | |
} | |
else | |
{ | |
job.Status = "Failed, site server not found."; | |
} | |
} | |
} | |
# | |
# Clean Example | |
# | |
private Job CreateJob(Order order) | |
{ | |
var computer = GetComputer(order.ComputerName, order.CustomerId); | |
computer.CustomerId = GetCustomerId(order.CustomerId, computer.CustomerId); | |
var serverAddress = GetServerAddress(order.ComputerName, computer.CustomerId); | |
var deploymentType = GetDeploymentType(order.MaterialNumber, computer.CustomerId); | |
var jobActions = CreateJobActions(deploymentType, order, serverAddress, computer); | |
var status = "created"; | |
Job job = new Job() | |
{ | |
ActivationTime = order.ClientLocalStartTime, | |
ComputerName = order.ComputerName, | |
OrderId = order.Id, | |
JobActions = jobActions, | |
Status = status, | |
}; | |
return job; | |
} | |
# | |
# Branching Strategies | |
# | |
* If-Else | |
* Local Functions (pass functions as a parameters -> callback functions) | |
* Try-Catch | |
* Monads | |
# | |
# If-Else Example | |
# | |
private Job CreateJob(order order) | |
{ | |
var errorMessage = ValidateComputer(order.ComputerName); | |
if (errorMessage != "") | |
{ | |
return new Job() | |
{ | |
ActivationTime = order.ClientLocalStartTime, | |
ComputerName = order.ComputerName, | |
orderId = order.Id, | |
Status = errorMessage, | |
}; | |
} | |
var computer = GetComputer(order.ComputerName, order.CustomerId); | |
computer.CustomerId = GetCustomerId(order.CustomerId, computer.CustomerId); | |
var serverAddress = GetServerAddress(order.ComputerName, computer.CustomerId); | |
var deploymentType = GetDeploymentType(order.MaterialNumber, computer.CustomerId); | |
return new Job() | |
{ | |
ActivationTime = order.ClientLocalStartTime, | |
ComputerName = order.ComputerName, | |
OrderId = order.Id, | |
JobActions = CreateJobActions(deploymentType, order, serverAddress, computer), | |
Status = "created", | |
} | |
} | |
# | |
# Try-Catch Example | |
# | |
private Job CreateJob(order order) | |
{ | |
Job job = new Job() | |
{ | |
ActivationTime = order.ClientLocalStartTime, | |
ComputerName = order.ComputerName, | |
orderId = order.Id, | |
}; | |
try // happy path | |
{ | |
var computer = GetComputer(order.ComputerName, order.CustomerId); | |
computer.CustomerId = GetCustomerId(order.CustomerId, computer.CustomerId); | |
var serverAddress = GetServerAddress(order.ComputerName, computer.CustomerId); | |
var deploymentType = GetDeploymentType(order.MaterialNumber, computer.CustomerId); | |
job.JobActions = CreateJobActions(deploymentType, order, serverAddress, computer); | |
job.Status = "created"; | |
} | |
catch (ComputerNotValid ex) // bad path | |
{ | |
job.Status = "Failed, computer invalid"; | |
} | |
catch (ComputerNotFound ex) // bad path | |
{ | |
job.Status = "Failed, computer not found"; | |
} | |
catch (NoSiteServerAddress ex) // bad path | |
{ | |
job.Status = "Failed, computer has no site server address"; | |
} | |
return job; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment