Skip to content

Commit

Permalink
Merge pull request #5 from Agomik/conway-smell
Browse files Browse the repository at this point in the history
Tightly coupled teams smell and sniffer implemented
  • Loading branch information
Agomik committed Oct 26, 2023
2 parents fc45720 + 4605aa6 commit 7445586
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 19 deletions.
9 changes: 7 additions & 2 deletions microfreshener/core/analyser/builder.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from .analyser import MicroToscaAnalyser
from .costants import SMELL_WOBBLY_SERVICE_INTERACTION_SMELL, SMELL_SHARED_PERSISTENCY, SMELL_SINGLE_LAYER_TEAMS, \
SMELL_MULTIPLE_SERVICES_IN_ONE_CONTAINER, SMELL_ESB_MISUSE
SMELL_MULTIPLE_SERVICES_IN_ONE_CONTAINER, SMELL_ESB_MISUSE, SMELL_TIGHTLY_COUPLED_TEAMS
from .sniffer import EndpointBasedServiceInteractionSmellSniffer, NoApiGatewaySmellSniffer, \
WobblyServiceInteractionSmellSniffer, SharedPersistencySmellSniffer, SingleLayerTeamsSmellSniffer, \
MultipleServicesInOneContainerSmellSniffer
MultipleServicesInOneContainerSmellSniffer, TightlyCoupledTeamsSmellSniffer

from .constant import SMELL_ENDPOINT_BASED_SERVICE_INTERACTION, SMELL_NO_API_GATEWAY

Expand All @@ -29,6 +29,8 @@ def add_smell(self, smell: str):
self.analyser.add_node_smell_sniffer(SharedPersistencySmellSniffer())
elif smell == SMELL_SINGLE_LAYER_TEAMS or smell == 7:
self.analyser.add_group_smell_sniffer(SingleLayerTeamsSmellSniffer(self.micro_model))
elif smell == SMELL_TIGHTLY_COUPLED_TEAMS or smell == 8:
self.analyser.add_group_smell_sniffer(TightlyCoupledTeamsSmellSniffer(self.micro_model))
else:
raise ValueError('Smell {} not recognized'.format(smell))
return self
Expand All @@ -48,6 +50,8 @@ def ignore_smell_for_node(self, node, smell:int):
self.analyser.add_node_smell_sniffer(SharedPersistencySmellSniffer())
elif smell == SMELL_SINGLE_LAYER_TEAMS:
self.analyser.add_group_smell_sniffer(SingleLayerTeamsSmellSniffer(self.micro_model))
elif smell == SMELL_TIGHTLY_COUPLED_TEAMS:
self.analyser.add_group_smell_sniffer(TightlyCoupledTeamsSmellSniffer(self.micro_model))
else:
raise ValueError('Smell {} not recognized'.format(smell))
return self
Expand All @@ -58,6 +62,7 @@ def add_all_sniffers(self):
self.analyser.add_node_smell_sniffer(SharedPersistencySmellSniffer())
self.analyser.add_node_smell_sniffer(MultipleServicesInOneContainerSmellSniffer())
self.analyser.add_group_smell_sniffer(SingleLayerTeamsSmellSniffer(self.micro_model))
self.analyser.add_group_smell_sniffer(TightlyCoupledTeamsSmellSniffer(self.micro_model))
self.analyser.add_group_smell_sniffer(NoApiGatewaySmellSniffer(self.micro_model))
return self

Expand Down
8 changes: 4 additions & 4 deletions microfreshener/core/analyser/costants.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
SMELLS_NAME = SMELL_ENDPOINT_BASED_SERVICE_INTERACTION, SMELL_WOBBLY_SERVICE_INTERACTION_SMELL, SMELL_SHARED_PERSISTENCY, SMELL_NO_API_GATEWAY, SMELL_SINGLE_LAYER_TEAMS, SMELL_MULTIPLE_SERVICES_IN_ONE_CONTAINER, SMELL_ESB_MISUSE =\
"Endpoint-based-service-interaction", "Wobbly-service-interaction", "Shared-persistency", "No-api-gateway", "Single-layer-teams", "Multiple-services-in-one-container", "ESB-misuse"
SMELLS_NAME = SMELL_ENDPOINT_BASED_SERVICE_INTERACTION, SMELL_WOBBLY_SERVICE_INTERACTION_SMELL, SMELL_SHARED_PERSISTENCY, SMELL_NO_API_GATEWAY, SMELL_SINGLE_LAYER_TEAMS, SMELL_MULTIPLE_SERVICES_IN_ONE_CONTAINER, SMELL_ESB_MISUSE, SMELL_TIGHTLY_COUPLED_TEAMS =\
"Endpoint-based-service-interaction", "Wobbly-service-interaction", "Shared-persistency", "No-api-gateway", "Single-layer-teams", "Multiple-services-in-one-container", "ESB-misuse", "Tightly-coupled-teams"

REFACTORING_NAMES = REFACTORING_ADD_SERVICE_DISCOVERY, REFACTORING_ADD_MESSAGE_ROUTER, REFACTORING_ADD_MESSAGE_BROKER, REFACTORING_ADD_CIRCUIT_BREAKER, REFACTORING_USE_TIMEOUT, REFACTORING_MERGE_SERVICES, REFACTORING_SPLIT_DATABASE, REFACTORING_ADD_DATA_MANAGER, REFACTORING_ADD_API_GATEWAY, REFACTORING_SPLIT_SERVICES, REFACTORING_SPLIT_TEAMS_BY_SERVICE =\
"Add-service-discovery", "Add-message-router", "Add-message-broker", "Add-circuit-breaker", "Use-timeout", "Merge-service", "Split-Datastore", "Add-data-manager", "Add-api-gateway", "Split-service-in-two-pods", "Split-teams-by-service"
REFACTORING_NAMES = REFACTORING_ADD_SERVICE_DISCOVERY, REFACTORING_ADD_MESSAGE_ROUTER, REFACTORING_ADD_MESSAGE_BROKER, REFACTORING_ADD_CIRCUIT_BREAKER, REFACTORING_USE_TIMEOUT, REFACTORING_MERGE_SERVICES, REFACTORING_SPLIT_DATABASE, REFACTORING_ADD_DATA_MANAGER, REFACTORING_ADD_API_GATEWAY, REFACTORING_SPLIT_SERVICES, REFACTORING_SPLIT_TEAMS_BY_SERVICE, REFACTORING_SPLIT_TEAMS_BY_COUPLING =\
"Add-service-discovery", "Add-message-router", "Add-message-broker", "Add-circuit-breaker", "Use-timeout", "Merge-service", "Split-Datastore", "Add-data-manager", "Add-api-gateway", "Split-service-in-two-pods", "Split-teams-by-service", "Split-teams-by-coupling"
23 changes: 19 additions & 4 deletions microfreshener/core/analyser/smell.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from ..model import Relationship
from ..model import nodes
from .costants import SMELL_ENDPOINT_BASED_SERVICE_INTERACTION, SMELL_NO_API_GATEWAY, SMELL_SHARED_PERSISTENCY, SMELL_WOBBLY_SERVICE_INTERACTION_SMELL, SMELL_SINGLE_LAYER_TEAMS, SMELL_MULTIPLE_SERVICES_IN_ONE_CONTAINER
from .costants import REFACTORING_ADD_SERVICE_DISCOVERY, REFACTORING_ADD_MESSAGE_ROUTER, REFACTORING_ADD_MESSAGE_BROKER, REFACTORING_ADD_CIRCUIT_BREAKER, REFACTORING_USE_TIMEOUT, REFACTORING_MERGE_SERVICES, REFACTORING_SPLIT_DATABASE, REFACTORING_ADD_DATA_MANAGER, REFACTORING_ADD_API_GATEWAY, REFACTORING_SPLIT_SERVICES, REFACTORING_SPLIT_TEAMS_BY_SERVICE
from .costants import SMELL_ENDPOINT_BASED_SERVICE_INTERACTION, SMELL_NO_API_GATEWAY, SMELL_SHARED_PERSISTENCY, SMELL_WOBBLY_SERVICE_INTERACTION_SMELL, SMELL_SINGLE_LAYER_TEAMS, SMELL_MULTIPLE_SERVICES_IN_ONE_CONTAINER, SMELL_TIGHTLY_COUPLED_TEAMS
from .costants import REFACTORING_ADD_SERVICE_DISCOVERY, REFACTORING_ADD_MESSAGE_ROUTER, REFACTORING_ADD_MESSAGE_BROKER, REFACTORING_ADD_CIRCUIT_BREAKER, REFACTORING_USE_TIMEOUT, REFACTORING_MERGE_SERVICES, REFACTORING_SPLIT_DATABASE, REFACTORING_ADD_DATA_MANAGER, REFACTORING_ADD_API_GATEWAY, REFACTORING_SPLIT_SERVICES, REFACTORING_SPLIT_TEAMS_BY_SERVICE, REFACTORING_SPLIT_TEAMS_BY_COUPLING
class Smell(object):

def __init__(self, name):
Expand Down Expand Up @@ -138,7 +138,7 @@ def __init__(self, group):
super(SingleLayerTeamsSmell, self).__init__(self.name, group)

def __str__(self):
return 'CrossTeamDataManagement({})'.format(super(SingleLayerTeamsSmell, self).__str__())
return 'SingleLayerTeams({})'.format(super(SingleLayerTeamsSmell, self).__str__())

def to_dict(self):
sup_dict = super(SingleLayerTeamsSmell, self).to_dict()
Expand All @@ -159,4 +159,19 @@ def to_dict(self):
sup_dict = super(MultipleServicesInOneContainerSmell, self).to_dict()
return {**sup_dict, **{"refactorings": [
{"name": REFACTORING_SPLIT_SERVICES, "description": "Split containers in two pods"},
]}}
]}}

class TightlyCoupledTeamsSmell(GroupSmell):
name: str = SMELL_TIGHTLY_COUPLED_TEAMS

def __init__(self, group):
super(TightlyCoupledTeamsSmell, self).__init__(self.name, group)

def __str__(self):
return 'TightlyCoupledTeams({})'.format(super(TightlyCoupledTeamsSmell, self).__str__())

def to_dict(self):
sup_dict = super(TightlyCoupledTeamsSmell, self).to_dict()
return {**sup_dict, **{"refactorings": [
{"name": REFACTORING_SPLIT_TEAMS_BY_COUPLING, "description": "Split the teams by coupling."},
]}}
57 changes: 48 additions & 9 deletions microfreshener/core/analyser/sniffer.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

from abc import ABCMeta, abstractmethod
from .smell import NodeSmell, SingleLayerTeamsSmell, EndpointBasedServiceInteractionSmell, NoApiGatewaySmell, \
from .smell import NodeSmell, SingleLayerTeamsSmell, EndpointBasedServiceInteractionSmell, NoApiGatewaySmell, TightlyCoupledTeamsSmell, \
WobblyServiceInteractionSmell, SharedPersistencySmell, MultipleServicesInOneContainerSmell
from ..model import Service, Datastore, CommunicationPattern, MessageRouter, Compute
from ..model.type import MICROTOSCA_NODES_MESSAGE_ROUTER
Expand All @@ -10,6 +10,7 @@

from ..helper.decorator import visitor

import sys

class NodeSmellSniffer(metaclass=ABCMeta):

Expand Down Expand Up @@ -102,24 +103,23 @@ def snif(self, group: Edge) -> [NoApiGatewaySmell]:

class SingleLayerTeamsSmellSniffer(GroupSmellSniffer):

def __str__(self):
return 'SingleLayerTeamsSmellSniffer({})'.format(super(GroupSmellSniffer, self).__str__())

@visitor(Team)
def snif(self, group: Team)->SingleLayerTeamsSmell:
def snif(self, group: Team) -> SingleLayerTeamsSmell:
smell = SingleLayerTeamsSmell(group)
for node in group.members:
for relationship in node.interactions:
source_node = relationship.source
target_node = relationship.target
source_squad = self.micro_model.squad_of(source_node)
target_squad = self.micro_model.squad_of(target_node)
if (target_squad is None or
(isinstance(source_node, Service) and isinstance(target_node, Datastore)
and source_squad != target_squad)):
if (target_squad is not None and isinstance(source_node, Service) and
isinstance(target_node, Datastore) and source_squad != target_squad):
smell.addLinkCause(relationship)
return smell

def __str__(self):
return 'SingleLayerTeamsSmell({})'.format(super(GroupSmellSniffer, self).__str__())

class MultipleServicesInOneContainerSmellSniffer(NodeSmellSniffer):

def __str__(self):
Expand All @@ -136,4 +136,43 @@ def snif(self, node) -> MultipleServicesInOneContainerSmell:

@visitor(MicroToscaModel)
def snif(self, micro_model):
print("visiting all the nodes in the graph")
print("visiting all the nodes in the graph")

class TightlyCoupledTeamsSmellSniffer(GroupSmellSniffer):

def __str__(self):
return 'TightlyCoupledTeamsSmellSniffer({})'.format(super(GroupSmellSniffer, self).__str__())

GRAPH_DEGREE_COUPLING = "graph-degree-coupling"

def _get_coupling_measure(self, criterion):
if criterion == self.GRAPH_DEGREE_COUPLING:
return self._graph_degree_coupling

def _graph_degree_coupling(self, link):
source_node = link.source
target_node = link.target
if source_node != target_node:
return 1
else:
return sys.maxsize

@visitor(Team)
def snif(self, group: Team) -> TightlyCoupledTeamsSmell:
smell = TightlyCoupledTeamsSmell(group)
coupling = self._get_coupling_measure(self.GRAPH_DEGREE_COUPLING)
for node in group.members:
coupled_squads = { group: 0 }
for relationship in (node.interactions + node.incoming_interactions):
source_node = relationship.source
source_squad = self.micro_model.squad_of(source_node)
target_node = relationship.target
target_squad = self.micro_model.squad_of(target_node)
if node is source_node and target_squad is not None:
coupled_squads[target_squad] = coupled_squads.get(target_squad, 0) + coupling(relationship)
elif node is target_node and source_squad is not None:
coupled_squads[source_squad] = coupled_squads.get(source_squad, 0) + coupling(relationship)
most_interacting_squads = [squad for squad, count in coupled_squads.items() if count == max(coupled_squads.values())]
if group not in most_interacting_squads:
smell.addNodeCause(node)
return smell

0 comments on commit 7445586

Please sign in to comment.