Domain-Driven Development with Context::Singleton
- name: Branislav Zahradník
- pause id: BARNEY
- github: happy-barney
- email: happy.barney@gmail.com
- pm group: Brno.pm
- working at GoodData
Why domain-driven development ?
- focus on business side of software development
- emphasize business / development cooperation
- emphasize domain expert's contribution
- emphasize minimalistic design (bounded context)
How to measure software quality
- number of bugs ?
- maintainance effort ?
- evolution effor ?
Explain DDD - Bounded context
- semantically related component
- example (taken from Domain-Driven Design Destilled)
- product
- backlog item
- sprint
- release
- bounded context looks like µservice
- reuseable (context mapping)
Explain DDD - domain language
- describes applicable scenarios using domain terms
- common for both domain experts (business) and developers
- same term can have different meaning in different BC
Explain DDD - domain event
- record of something business important
- product of domain command
- eventual consistency
- can be observed
Explain DDD - tests
- test case are described using domain language
- assert domain events
- domain expert can write or verify test case
- tests are entry point into system
DDD with Context::Singleton
- frame - flat model
- proclaim / deduce - value objects
- deduce - domain command / domain event
- trigger - observing domain events
Example - event listener
trigger 'project::id' => sub {
logger->mdc->set (PROJECT_ID => $_[0])
}
Example - project backup using remote storage
contrive 'backup::project' => (
dep => [ qw[ project::id backup::driver ] ],
);
contrive 'backup::driver' => (
class => 'Backup::Storage',
dep => [ 'backup::storage::username', 'backup::storage::password' ],
);
proclaim 'Backup::Storage' => 'Backup::Storage::S3::AWS';
Example - ... timtowdi
contrive 'net-amazon-s3-access-key' => (
dep => [ 'backup::storage::username' ],
);
contrive 'net-amazon-s3-sercet-key' => (
dep => [ 'backup::storage::password' ],
);
contrive 'net-amazon-s3' => (
class => 'Net::Amazon::S3',
dep => {
aws_access_key => 'net-amazon-s3-access-key',
aws_secret_key => 'net-amazon-s3-sercet-key',
},
)
Example - tests
- proclaim - inject state (mocks)
- deduce 'domain::command'
- assert 'domain-event' ...
proclaim 'net-amazon-s3-access-key' => 'Dummy';
proclaim 'net-amazon-s3-secret-key' => 'Dummy';
proclaim 'net-amazon-s3-bucket' => 'dummy-bucket';
proclaim 'net-amazon-s3-bucket-region' => 'ca-central-1';
proclaim 'net-amazon-s3-object' => 'some/object';
proclaim 'net-amazon-s3-expire-in' => 7_200;
proclaim 'now' => '2010-01-01 12:36:54';
ok deduce ('net-amazon-s3-object-signed-uri'), ...;
Example - API versioninig
contrive "parser/v1.0" => ( dep => [ "vnd.v1.0+json" ], ... )
contrive "parser/v2.0" => ( dep => [ "vnd.v2.0+json" ], ... )
contrive "user::login" => (
dep => [ "parser/v1.0" ],
as => sub { $_[0]->{login} },
);
contrive "user::login" => (
dep => [ "parser/v2.0" ],
as => sub { $_[0]->{user}{login_name} },
);
proclaim $request->{content_type} => 1;
deduce "authorize::user";
Example - API versioning
contrive "builder/v1.0" ...;
contrive "builder/v2.0" ...;
contrive "outout/user/v1.0" => (
dep => [ "builder/v1.0", "user::login", "user::password" ],
as => sub {
$_[0]->build ({
login => $_[1],
password => $_[1],
});
},
);
contrive "outout/user/v2.0" => (
dep => [ "builder/v2.0", "user::login", "user::password" ],
as => sub {
$_[0]->build ({ user => {
login_name => $_[1],
password => $_[1],
}});
},
);
Books
- Domain-Driven Design Destilled by Vaughn Vernon
- Implementing Domain-Driven Design by Vaughn Vernon
- Domain-Driven Design - Tackling Complexity in the Heart of Software by Eric Evans