New Design


New Design

Framework needs to do:

Web Layer:

  1. Map HTTP request to Command objects.(auto)
  2. Map input to the Request of the use case.
  3. Call the use case
  4. Map output of the use case back to HTTP.
  5. Return HTTP response.(auto)

Persistence Layer:

  1. Take input.
  2. Map input into database format.
  3. Send input to the database.
  4. Map database output into entity format.
  5. Return output.

Kernel needs to do:

  1. Take input
  2. Do input validation and business validation.
  3. Manipulate entity state
  4. return output.

When we need a new framework, we could only implements the same codes in the new framework, and reuse the kernel. Because the framework codes do not include business rules and the most works are transfering data format, run use case code from the kernel, so new team memenbers can still easily implement the code. And the new team memenbers can be contractors.

The framework can also be replaced by a test framework, the test framework may read test cases from Excel which is easy for business men to write test case.

@startuml

box "Client" #FDFD96
actor actor as "Payroll"
end box

box "Framework" #A7C7E7
    control controller as "EnterPayrollAdjustmentsController"
    control service as "EnterPayrollAdjustmentsService"
    control repositoryService as "LoadEmployeePositionAdjustmentsService"
    control positionRepositoryService as "LoadEmployeePositionsService"
end box
box "Kernel" #FFC0CB
    control request as "FindEmployeePositionAdjustmentsRequest"
    boundary useCase as "FindEmployeePositionAdjustmentsUseCase"
    entity district as "District"
    entity payroll as "payroll"
    entity employee as "Employee"
    boundary repository as "LoadEmployeePositionAdjustmentsRepository"
    boundary positionRepository as "LoadEmployeePositionsRepository"
end box

actor -> controller : findEmployeePayrollAdjustments(\n\tEnterPayrollAdjustmentsFindCommand command\n)
activate controller
    note right
      Command: data binding
      The command class may inherit the request class
      to avoid data mapping between layers and to develop
      application quickly, and it also includes the UI information.
    end note
    controller -> service: findEmployeePayrollAdjustments(command)
    activate service
        note right
          Service: transaction management
        end note
        service -> request: request = new FindEmployeePositionAdjustmentsRequest(\ncountyNbr, districtNbr,\n fiscalYear, payrollCyle, payrollType,\n employeeNbr)
            note right
               Data Mapping, use rich constructor.
               Making the request class immutable, once constructed successfully,
               we can be sure that the state is valid and cannot be changed to 
               something invalid.
            end note
        activate request
            request -> district: district = new District(countyNbr, districtNbr) 
            request -> request: setDistrict(district)
            request -> payroll: payroll = new Payroll(fiscalYear, payrollCyle, payrollType)
            request -> request: setPayroll(payroll)
            request -> employee: employee = new Employee(employeeNbr)
            request -> request: setEmployee(employee)
        deactivate request
            service -> repositoryService: repository = new LoadEmployeePositionAdjustmentsService()
            note right
                LoadEmployeePositionAdjustmentsService implements
                LoadEmployeePositionAdjustmentsRepository;
                LoadEmployeePositionsService implements
                LoadEmployeePositionsRepository
            end note
            service -> positionRepositoryService: positionRepository = new LoadEmployeePositionsService()
            note left
                Dependency Injection
            end note
            service -> useCase : setRepositories(repository, positionRepository)
            note right
                Singleton
                Use a factory to create a use case object
                by using the singleton design pattern.
            end note
            service -> useCase : execute(request)
        activate useCase
            useCase -> useCase : validate(request)
            note right
                Input validation, and business validation.
                Can you see it? Can you use it? Can you do it?
            end note
            useCase -> request: district = getDistrict()
            useCase -> request: payroll = getPayroll()
            useCase -> request: employee = getEmployee()
            useCase -> positionRepository: loadPositions(employee)
            note left
               In order to avoid developer changing employee's property value,
               1. make the Employee class immutable
               2. use interface as parameter type
               3. write unit tests to make sure the argument is not changed.
               4. make development rules developers should follow.
            end note
            activate positionRepository
            useCase <- positionRepository: Collection<Position> positions
            note left
               Data Mapping, use rich constructor.
              Converts relational database data record to objects.
            end note
            deactivate positionRepository
            useCase -> repository: loadAdjustments(district, employee, payroll)
            activate repository
            useCase <- repository: Collection<PayrollAdjustment> payrollAdjustments
            note left
               Data Mapping, use rich constructor.
               Converts relational database data record to objects.
            end note
            deactivate repository
            service <- useCase : Collection<PayrollAdjustment> payrollAdjustments
        deactivate useCase
    controller <- service: Collection<PayrollAdjustment> payrollAdjustments
    deactivate service
actor <- controller : Collection<PayrollAdjustment> payrollAdjustments
deactivate controller
@enduml