refactoring domain driven design way

58
REFACTORING CODE DOMAIN DRIVEN DESIGN WAY Supported By:

Upload: andi-pangeran

Post on 14-Apr-2017

150 views

Category:

Software


0 download

TRANSCRIPT

Page 1: Refactoring domain driven design way

REFACTORING CODEDOMAIN DRIVEN DESIGN WAY

Supported By:

Page 2: Refactoring domain driven design way

Supported By:

I’M ANDI PANGERAN

@andi_pangerancybercoding.wordpress.com

Page 3: Refactoring domain driven design way

Supported By:

AGENDA PRACTICE HOW TO REFACTORING CODE BASED ON DOMAIN

DRIVEN DESIGN MINDSET

PRACTICE HOW TO WRITE UNIT TESTING

Page 4: Refactoring domain driven design way

Supported By:

JOKO

Individual who has significant expertise in the domain of the system being developed.

DOMAIN EXPERT

MEET OUR ACTOR

Page 5: Refactoring domain driven design way

Supported By:

BENTO

Individual who has spend all of his life for coding.

PROGRAMMER

MEET OUR ACTOR

Page 6: Refactoring domain driven design way

Supported By:

PRACTICE

Page 7: Refactoring domain driven design way

Supported By:

Protect your invariantsREFACTORING

Page 8: Refactoring domain driven design way

Supported By:

JOKO

PRACTICE

“A customer mustalways have anemail address.”

Page 9: Refactoring domain driven design way

Supported By:

PRACTICE

BENTO

public class Customer {

private String email;

public String getEmail() { return email; }

public void setEmail(String email) { this.email = email; }}

Page 10: Refactoring domain driven design way

Supported By:

PRACTICE

BENTO

public class CostumerTest {

@Test public void should_always_have_an_email() {

Customer customer = new Customer(); assertEquals(customer.getEmail(), "[email protected]"); }}

TEST FAIL

Page 11: Refactoring domain driven design way

Supported By:

PRACTICE

BENTO

public class CostumerTest {

@Test public void should_always_have_an_email() {

Customer customer = new Customer(); customer.setEmail("[email protected]"); assertEquals(customer.getEmail(), "[email protected]"); }}

TEST PASSED

Page 12: Refactoring domain driven design way

Supported By:

PRACTICE

BENTO

public class Customer {

private String email;

public Customer(String email) { this.email = email; }}

Page 13: Refactoring domain driven design way

Supported By:

PRACTICE

BENTO

public class CostumerTest {

@Test public void should_always_have_an_email() {

Customer customer = new Customer("[email protected]"); assertEquals(customer.getEmail(), "[email protected]"); }}

TEST PASSED

Page 14: Refactoring domain driven design way

Supported By:

Use object as concistency boundariesREFACTORING

Page 15: Refactoring domain driven design way

Supported By:

JOKO

PRACTICE

“later prospective customer can be upgraded to paying customer“

“Paying customer must have phone number”

“we have two type of customer, prospective and paying customer.”

Page 16: Refactoring domain driven design way

Supported By:

PRACTICE

BENTO

public class ProspectiveCustomer extends Customer { /* other attribute */}

public class PayingCustomer extends Customer {

/* other attribute */ private String phone;

public PayingCustomer(String email, String phone) { super(email); this.phone = phone; }}

Page 17: Refactoring domain driven design way

Supported By:

PRACTICE

BENTO

@Testpublic void upgraded_prospective_paying() {

ProspectiveCustomer pcustomer = new ProspectiveCustomer("[email protected]");

assertEquals(pcustomer.getEmail(), "[email protected]");

PayingCustomer payCustomer = new PayingCustomer(pcustomer.getName(), “0852xxx”);

assertEquals(payCustomer.getEmail(), "[email protected]"); assertEquals(payCustomer.getPhone(), " 0852xxx");}

TEST FAIL

Page 18: Refactoring domain driven design way

Supported By:

PRACTICE

BENTO

@Testpublic void upgraded_prospective_paying() {

ProspectiveCustomer pcustomer = new ProspectiveCustomer("[email protected]");

assertEquals(pcustomer.getEmail(), "[email protected]");

PayingCustomer payCustomer = new PayingCustomer(pcustomer.getEmail(), “0852xxx”);

assertEquals(payCustomer.getEmail(), "[email protected]"); assertEquals(payCustomer.getPhone(), " 0852xxx");}

TEST PASSED

Page 19: Refactoring domain driven design way

Supported By:

PRACTICE

BENTO

public class ProspectiveCustomer extends Customer { /* other attribute */ public PayingCustomer upgradeToPayingCustomer(String phone) { return new PayingCustomer(this.email, phone); }}

public class PayingCustomer extends Customer {

/* other attribute */ private String phone;

public PayingCustomer(String email, String phone) { super(email); this.phone = phone; }}

Page 20: Refactoring domain driven design way

Supported By:

PRACTICE

BENTO

@Testpublic void upgraded_prospective_paying() {

ProspectiveCustomer pcustomer = new ProspectiveCustomer("[email protected]");

assertEquals(pcustomer.getEmail(), "[email protected]");

PayingCustomer payCustomer = pcostumer.upgradeToPaying(“0852xxx”);

assertEquals(payCustomer.getEmail(), "[email protected]"); assertEquals(payCustomer.getPhone(), " 0852xxx");}

TEST PASSED

Page 21: Refactoring domain driven design way

Supported By:

JOKO

PRACTICE

“Paying customer must always have a valid phone number”

Page 22: Refactoring domain driven design way

Supported By:

PRACTICE

BENTO

@Testpublic void upgraded_prospective_paying() {

PayingCustomer payCustomer = new PayingCustomer(“[email protected]", “badphonenumber”);

exception.expect(IllegalArgumentException.class);}

TEST FAIL

Page 23: Refactoring domain driven design way

Supported By:

PRACTICE

BENTO

public class PayingCustomer extends Customer {

/* other attribute */ private String phone;

public PayingCustomer(String email, String phone) { super(email);

if (/* validation stuff of phone*/ ) { throw new IllegalArgumentException(); } this.phone = phone; }}

TEST PASSED

Page 24: Refactoring domain driven design way

Supported By:

PRACTICE

BENTO

public class PayingCustomer extends Customer {

/* other attribute */ private String phone;

public PayingCustomer(String email, String phone) { super(email);

if (/* validation stuff of phone*/ ) { throw new IllegalArgumentException(); } this.phone = phone; }}

TEST PASSED

Single Responsibilit

y Princip

le

Violates

Page 25: Refactoring domain driven design way

Supported By:

Encapulate state and behavior with value object

REFACTORING

Page 26: Refactoring domain driven design way

Supported By:

PRACTICE Intro to value object

Intro to immutable object

Page 27: Refactoring domain driven design way

Supported By:

PRACTICE

BENTO

public class PhoneNumber {

private final String phone;

public PhoneNumber(String phone) { if (/* validation stuff of phone*/ ) { throw new IllegalArgumentException(); } this.phone = phone; }

public String getPhone() { { return this.phone; }}

Page 28: Refactoring domain driven design way

Supported By:

PRACTICE

BENTO

public class PayingCustomer extends Customer {

/* other attribute */ private PhoneNumber phone;

public PayingCustomer(String email, PhoneNumber phone) { super(email); this.phone = phone; }}

TEST PASSED

Page 29: Refactoring domain driven design way

Supported By:

PRACTICE

BENTO

@Testpublic void upgraded_prospective_paying() {

PayingCustomer payCustomer = new PayingCustomer(“[email protected]", new

PhoneNumber(“badphonenumber”));

exception.expect(IllegalArgumentException.class);}

TEST PASSED

Page 30: Refactoring domain driven design way

Supported By:

Encapulate OperationsREFACTORING

Page 31: Refactoring domain driven design way

Supported By:

JOKO

PRACTICE

“a Customer orders product and pays for them”

Page 32: Refactoring domain driven design way

Supported By:

Service.create

BENTO

Order order = new Order();order.setCustomer(customer);order.setProducts(products);order.setStatus(PAYMENTSTATUS.UNPAID);

order.setPaidAmount(500);order.setPaidCurrency(CURRENCY.IDR);order.setStatus(PAYMENTSTATUS.PAID);

PRACTICE

Service.pay

Page 33: Refactoring domain driven design way

Supported By:

Service.create

BENTO

Order order = new Order();order.setCustomer(customer);order.setProducts(products);order.setStatus(new PaymentStatus(PAYMENTSTATUS.UNPAID));

order.setPaidAmount(500);order.setPaidCurrency(CURRENCY.IDR);order.setStatus(new PaymentStatus(PAYMENTSTATUS.PAID));

PRACTICE

Service.pay

Page 34: Refactoring domain driven design way

Supported By:

Service.create

BENTO

Order order = new Order();order.setCustomer(customer);order.setProducts(products);order.setStatus(new PaymentStatus(PAYMENTSTATUS.UNPAID));

order.setPaidMonetary(new Money(500, CURRENCY.IDR));order.setStatus(new PaymentStatus(PAYMENTSTATUS.PAID));

PRACTICE

Service.pay

Page 35: Refactoring domain driven design way

Supported By:

Service.create

BENTO

Order order = new Order(customer, products);//set payment status in order constructor

order.setPaidMonetary(new Money(500, CURRENCY.IDR));order.setStatus(new PaymentStatus(PAYMENTSTATUS.PAID));

PRACTICE

Service.pay

Page 36: Refactoring domain driven design way

Supported By:

Service.create

BENTO

Order order = new Order(customer, products);//set payment status in order constructor

order.pay (new Money(500, CURRENCY.IDR));//set payment status in pay procedure

PRACTICE

Service.pay

Page 37: Refactoring domain driven design way

Supported By:

Use SpeficationREFACTORING

Page 38: Refactoring domain driven design way

Supported By:

JOKO

PRACTICE

“premium customers get special offers”

Page 39: Refactoring domain driven design way

Supported By:

Service.premium

BENTO

If (customer.isPremium()) { //send offer}

PRACTICE

Page 40: Refactoring domain driven design way

Supported By:

JOKO

PRACTICE

“order 3 times to become premium customer”

Page 41: Refactoring domain driven design way

Supported By:

Specification Interface

BENTO

public interface Specification<T> { boolean isSatisfiedBy(T t); Class<T> getType();}

PRACTICE

Specification Abstractabstract public class AbstractSpecification<T> implements Specification<T> { @Override public boolean isSatisfiedBy(T t) { throw new NotImplementedException(); } /othercode}

Page 42: Refactoring domain driven design way

Supported By:

BENTO

public class CustomerIsPremium extends AbstractSpecification<Customer> {

private OrderRepository orderRepository

public CustomerIsPremium(OrderRepository orderRepository) {this.orderRepository = orderRepository;

} @Override public boolean isSatisfiedBy(Customer customer) {

int count = this.orderRepository.countByCustomer(customer.getId); return (count > 3); }}

PRACTICE

Page 43: Refactoring domain driven design way

Supported By:

PRACTICE

BENTO

@Testpublic void specification_customer_premium() {

CustomerIsPremium<Customer> spec= new CustomerIsPremium();

customer2order = … customer3order = …

assertFalse(spec.isSatisfiedBy(customer2order)); assertTrue(spec.isSatisfiedBy(customer3order));

}

TEST PASSED

Page 44: Refactoring domain driven design way

Supported By:

JOKO

PRACTICE

“different rules apply for different tenants.”

Page 45: Refactoring domain driven design way

Supported By:

BENTO

public class CustomerWith3OrdersIsPremium implement CustomerIsPremium {}

public class CustomerWith500kPremium implement CustomerIsPremium {}

public class CustomerWithSpecialRequiredmentPremium implement CustomerIsPremium {}

PRACTICESpecification Interface

public interface CustomerIsPremium extends Specification<Customer> { boolean isSatisfiedBy(Customer cust); }

Page 46: Refactoring domain driven design way

Supported By:

BENTO

PRACTICEpublic class SpecialOfferSender {

private CustomerIsPremium premiumSpec

public SpecialOfferSende(CustomerIsPremium premiumSpec) {this.orderRepository = orderRepository;

} public void sendOffersTo(Customer customer) { if (this.premiumSpec.isSatisfiedBy(customer)) {

//send offers}

}}

Page 47: Refactoring domain driven design way

Supported By:

Use Spefication for Object SelectionREFACTORING

Page 48: Refactoring domain driven design way

Supported By:

JOKO

PRACTICE

“get a list of all premium customers”

Page 49: Refactoring domain driven design way

Supported By:

PRACTICE Intro to predicate pattern

Collection predicate

JPA predicate

Page 50: Refactoring domain driven design way

Supported By:

BENTO

PRACTICESpecification Interfacepublic interface Specification<T> { boolean isSatisfiedBy(T t); Predicate toCollectionPredicate();}

@Override public Predicate toCollectionPredicate(Customer customer) {

Predicate<Customer> predicate = new predicate<Customer>() {

@Override public boolean apply(Customer input) { return this.isSatisfiedBy(input); } };return predicate;

}

Page 51: Refactoring domain driven design way

Supported By:

BENTO

PRACTICECustomerIsPremium<Customer> spec= new CustomerIsPremium();

List<Customer> allCustomer = …Collection<Customer> result =

Collections2.filter(allCustomer,

spec.toCollectionPredicate());

assertArrayEquals(result, … testarray);

TEST PASSED

Page 52: Refactoring domain driven design way

Supported By:

BENTO

PRACTICESpecification Interfacepublic interface Specification<T> { boolean isSatisfiedBy(T t); Predicate toCollectionPredicate(); Predicate toPredicate(Root<T> root, CriteriaBuilder cb);}

@Override public Predicate toPredicate (Root<Customer> root, CriteriaBuilder cb) {

return cb.and (cb.greaterThan(root.get(Customer_.order),3) );}

Page 53: Refactoring domain driven design way

Supported By:

BENTO

PRACTICEpublic class DBRepository { private EntityManager entityManager = ... public <T> List<T> findAllBySpecification(Specification<T> specification) { CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder(); // use specification.getType() to create a Root<T> instance CriteriaQuery<T> criteriaQuery = criteriaBuilder.createQuery(specification.getType()); Root<T> root = criteriaQuery.from(specification.getType()); // get predicate from specification Predicate predicate = specification.toPredicate(root, criteriaBuilder); // set predicate and execute query criteriaQuery.where(predicate); return entityManager.createQuery(criteriaQuery).getResultList(); }}

Page 54: Refactoring domain driven design way

Supported By:

BENTO

PRACTICECustomerIsPremium<Customer> spec= new CustomerIsPremium();

List<Customer> Customer = DBRepository. findAllBySpecification(spec);

assertArrayEquals(result, … testarray);

TEST PASSED

Page 55: Refactoring domain driven design way

Supported By:

SUMMARY Protect your invariants

Use object as consistency boundaries

Encapulate state and behavior with value object

Encapulate Operations

Use specification pattern

Use predicate pattern

Page 56: Refactoring domain driven design way

Supported By:

Questions ?

Page 57: Refactoring domain driven design way

Supported By:

Identity CTM for Refactoring, each monday

REFACTORING

Page 58: Refactoring domain driven design way

Supported By:

Thanks