Architecture & OOP

expectations vs reality

All images comes from pixabay

About ME

  • Señor Developer @ Orba
  • self-proclaimed code magician
  • mentor & consultant
  • devThursday keeper

 

Architectural pattern?

Mainstream architecture

  • Entity Model
  • Business logic inside services
  • Queries inside dao resource models
  • Entities used on the view layer
  • Single responsibility principle
  • 8k class
  • Class Explosion (Entity, Repostory, Resource Model, Service, Helper, View) 
  • View structure based on entities
  • Code fossilization
  • Hard to test

Mainstream architecture

"Testy przechodzą, muszę tylko przeklikać" :)

$product->getOrderableUnitsOfMeasure()

Resource Model - data access object?

class Mix {
    public function getCategoriesIds(int $mixId): array

    public function saveCategories(int $mixId, array $categoryIds): void 

    public function deleteAllCategories(int $mixId): void

    protected function deleteOldCategories(int $mixId, array $categoryIds): void

    protected function addNewCategories(int $mixId, array $categoryIds): void

    public function getShipToIds(int $mixId): array

    protected function addNewShipTo($mixId, array $shipToIds): void

    public function deleteAllShipTo(): void

    public function deleteOldShipTo(int $mixId, array $shipToIds): void

    public function saveShipTo(int $mixId, array $shipToIds): void
}

Layered Architectural Pattern

PRESENTATION

BUSINESS LOGIC

SERVICE

DATABASE

class Match {
    public $id;
    public $startTime;
    public $endtime;
    public $score;

    public function start();
    public function stop();

    public function getStartTime();
    public function getEndTime();

    public function setScore();
    public function getScore();

    public function isStarted();
    public function isEnded();

    public function getState();
}
class PreparedMatch {
    public $id;

    public getId(): int;
}
// $matchManager->prepareMatch(): PreparedMatch;
// $matchManager->start($match): StartedMatch;

class StartedMatch {
    public $id;
    public $startTime;
    public $score;
    
    public function setScore();
    public function getScore();
    public function getStartedTime();
}
// $matchManager->getStartedMatch();
// $matchManager->end($match): EndedMatch;

class EndedMatch {
    public $id;
    public $startTime;
    public $endtime;
    public $score;

    public function getStartTime();
    public function getEndTime();
    public function getScore();
}

// $matchManager->getEndedMatch();

OOP?

Reduce the Number of Arguments

 public function getSimulation(string $orderTypeCode, string $soldToPartyCode, 
string $salesOrganisationCode, string $distributionChannelCode,
string $divisionCode, string $plantCode, string $pricingDate, string $reference,
 string $conditionTypes, string $backendEndPoint, array $items): Simulation


public function getSimulation(SimulationRequest $simulationRequest): Simulation

Try not to use maps

$parameters = [
    'param1' => 'value1',
    'param2' => 'value2',
    'param3' => 'value3',
    'param4' => 'value4',
    'param5' => 'value5',
    'param6' => 'value6',
];
parameters = {
    param1 : 'value1',
    param2 : 'value2',
    param3 : 'value3',
    param4 : 'value4',
    param5 : 'value5',
    param6 : 'value6',
};

Reduce the Size of Methods

Split Large Classes

Send Messages to Components instead of to This

abstract class AbstractExampleDocument 
{
  // skip some code ...
  public void output(Example structure) 
  {
    if( null != structure )
    {
      this.format( structure );
    }
  }

  protected void format(Example structure);
}
class DefaultExampleDocument 
{
// skip some code ...
  public void output(Example structure) 
  {
    ExampleFormatter formatter = 
     (ExampleFormatter) manager.lookup(Roles.FORMATTER);
    if( null != structure ) 
    {
      formatter.format(structure);
    }
  }
}

Eliminate Implicit Parameter Passing

// Utils/Helper
public function isCurrentUserHaveAccess(int $websiteId): bool
{
    $role = $this->authSession->getUser()->getRole();
    
    foreach($role->getWebsites() as $website) {
        if($website->getId() === $websiteId) {
            return true;
        }
    }
    
    return false;
}

KEEP STRICT TYPING

/**
 * @return bool|array
 */
public function isValid()

/**
 * @return array|null
 */
public function  getData()

/**
 * @return Simulation|null
 */
public function getSimulation()


KEEP STRICT TYPING

public function isValid(): bool
public function getErrors(): array

public function getData(): array
{
    return $this->data?? [];
}

public function getSimulation(): Simulation
{
    try {
        return $this->resource->getSimulation();
    } catch (\Throwable $t) {
        $this->warning('Unable to get Simulation', [$t])
        return DummySimulation();
    } 
}

There is no one right way

Explore the context

Do not mix businness code

with service code

Explore the context

Do not change code

Just change Your mind :)

Q&A

Architecture

By Paweł Radzikowski

Architecture

  • 324