Jdon Framework DocumentTable of Contents
Before Domain-Driven Design(DDD) appears , business logic was directly implemented by technical component , such as a service or a action and so on. these model objects were all anaemia model (The Biggest Flaw of Spring Web Applications), anaemia model just is a data collection, and Service components drive the anaemia models to complete some business logics. this model was called "Transaction script". Domain-driven design (DDD) is different from traditional script. all domain logics are in Domain model, Domain model drives technical components to do technical something that not about business, domain model is like DNA. So DDD architecture can focus on business domain model that is easy to understand, in the mean time, domain model also makes our software systems more scalable,and be better performance. DDD is an approach to developing software for complex needs by deeply connecting the implementation to an evolving model of the core business concepts, servral concepts are the Heart and Soul of OOD, these concepts you must understand before using jdon: Bounded context Aggregate Root Entities and Identity, Value Objects
In Memory Domain modeldomain model mainly includes the Entity,Value Object and Service that all focus on the business domain,it is nothing to do with the concrete technical implementation. but we need let them living in the technical system. In Jdon Framework, these domain models are marked with the @Model annotation, so they are in memory and send asynchronous events or message to the consumers .
DomainCacheInterceptor is between domain layer(domain model live in this layer) and repository layer, when we fetch a domain model(@model) from repository layer, DomainCacheInterceptor will intercept the invocation(locate the method such as "get*()"), and check the model object if be in the cache,if that is,DomainCacheInterceptor just return the cached Model object ,or else it will put the model object retrieved from repository/persistence datasource into the cache.
CacheInterceptor only work when you use jdon-struts: "CacheInterceptor" is between the presentation layer and business layer,when the presentation action invoke the "get*()" method of business service,the CacheInterceptor will intercept the invocation,it first checks that whether the domain model object already is the cache,if the model object is not in cache,CacheInterceptor will invoke the next interceptor which is in the interceptor chain,and finally the model object will be fetched from persistence datasource,the CacheInterceptor will gain the fetched model object and put it into the cache. how to let model in memory?jdon provides some annotations to assist developer to do that, at first you must annotate @Model with your Aggregate Root Entity that can be accessed from outside such technical components: @Model
public class MyModel {
private String userId;
private String name;
....
}
MyModel is a Aggregate Root Entity inside a bounded context. you can't create the entity object by "new MyModel()", only fetch it from repository, so jdon provides some annotations to configure in repository. Second step: configure in repository @Introduce(“modelCache”) is annotated in repository class: there are three annotations: @Component @Introduce and @Around. @Component is for normal components(technical or application), any class with @Component can be managed by IOC container. Jdon Ioc container is based on pico container, only supports class construt injection. @Introduce is about aop interceptor, that means introduces a interceptor into here(at front of the class instance), the interceptor name is modelCache that configured in aspect.xml packed with jdon. if you don't like the name ("modelCache"), you can modify it in aspect.xml . @Around is the target method that will be intercepted. target method can be like "getXXX" or "findById"(jdon allows any method name), and its return class type must be the entity annotated with @Model(this is a jdon constraint). in the mehtod annotated with @Around, you can fetch the model from Relation or NoSQL Database, you can use JDBC or other ORM ( JDBC is recommended). you can use jdon -jdbc lib. finally we can find a model(@Model) by these code: MyModel myModel = repository.getModel(new Long(100)); myModel object is actual proxy instance that created by our interceptor "modelCache", when you first times got it, "modelCache" will locate it in Cache, if miss it will load it from your repository, if hit it directly return the myModel. when you got a myModel, you can invoke its any methods, but if you call a method with(@Oncommand), it is different with direct method invoking.
Command and EventCommand or Event is a communication way between domain model and components, at first we need know how much components type there are in jdon. Basic componentsThere are five components model in Jdon Framework, they are: 1. Entity Model with @Model;
2. Service with @Service;
technical component
3. Component with @Component ;
technical component
4. Prodcuer-Consumer mode with @Send @Consumer;
5. Interceptor with @ Interceptor, need with @Introduce;
All in com.jdon.annotation.*
As mentioned above,Jdon Framework seperates the application architecture into two parts:Domain models and technical components . so these two components are our basic. we will often use them. Domain model is annotated with @Model , it is a aggregate root entity in a bounded context, you can got them using DDD. Component annotated with @Component is others except domain model. include technical or application, they lives in the IOC container. can autowiring injected each other by their constrution. dependency injection is tight coupling although it is better than inheritance, we can use loose coupling producer-consumer mode. Any invoking between aggregate root(AR) and Component must use producer-consumer mode. that keeps our business logics are loose coupling with technical infrastructure, these is a core function of jdon. Service annotated with @Service is mico service that represents bounded context, or a DDD service that across multiple entities. Service also like other components live in Ioc container, it can be @stateful or @poolable. Now we know: domain models(@Model) lives in the cache ,and the technical components(@Service/@Component) lives in the IOC container. their communications are producer-consumer. Component's lifecycles management is also important,jdon use pico container to manage their lifecycles, the container is also holded in the context container such as the ServeltContext or your application root. you can use @Inject to inject component into domain model. but it is only for introducing domain events, not recommended for other usage.
differnce with Command and EventWhen a Component/Service sends messages to a Domain Model(aggregate root), we call the message is a command, and when a domain model send message to a Component, it is reactive a event.: A command actions the behavior(startMatch) of a aggregate root(domain model), and a event happend in this behavior, the event will be sent to another aggregate root or a Component that maybe save it to repository. Jdon provide four kinds of asynchronous concurrency communication(Producer/Consumer) with these two models. 1. Component -----> model 2. model ------->Component 3. Compponent ------> Component 4. model------> model
Commandin a typal Web application , we have this flow: UI ---Command---> a aggregate root(AR) ---Events -->Component: A command will change a state in a AR, but in a web application there are many concurent request, contention will happen, There is a lot of research in computer science for managing this contention that boils down to 2 basic approaches. One is to provide mutual exclusion to the contended resource while the mutation takes place; the other is to take an optimistic strategy and swap in the changes if the underlying resource has not changed while you created the new copy.(see details: Single Writer Principle) contention: Blocking (lock ) mutual exclusion: Noblocking single writer: Jdon provides a nonblocking single writer to change AR's state, LMAX disruptor is its Infrastructure, if a AR's method annotated with @OnCommand , it is a action for command from UI. only a single thread call the method. @Model private int state = 100; @OnCommand("CommandtoEventA") //update root's state in non-blocking single thread way (Single Writer) . . } } this AggregateRootA is a Consumer, Producer is in Service or CommandHandler: Producer:
@Send("CommandtoEventA") Producer emits message to its Consumer by a asynchronous and non-blocking queue made by LMAX Disruptor's RingBuffer. @Send("CommandtoEventA") ---> @OnCommand("CommandtoEventA") client code: AggregateRootA myModel = repository.getModel(new Long(100)); AService aService = webappUtil.getService("aService"); aService.commandA("100", myModel, 200);//change AggregateRootA state to 200;
this invoking way is thread safe, and have high througout and lower lantency, it is like Actors model in Scala, or like Netty's IO. it is nonblocking and asynchronous. in these mode, there are two kinds of Consumer in jdon: @Send --> @OnCommand (1:1 Queue) difference between 'OnCommand' and 'OnEvent' is: when a event happend otherwhere comes in a aggregate root we regard this event as a command, and it will action a method annotated with @OnCommand, and in this method some events will happen. and they will action those methods annotated with @OnEvent.
Domain EventsDomain Events is that events that happend in domain, wo can call it is reactive event. Domain Events is essential to solve the interaction between the domain model and the technical component,Jdon apply domain events pattern to make domain model less coupling with technical components. domain events will react when a command comes in :
As the above diagram description,when Command comes in domain models trigger some domain events,the domain event will generate proper domain message,at last the messageListener will treat with the event,and assist with some operations infrastructure such as saving DB. Because a command will react a domain event, so if we saving these domain events, be equals to saving those commands, and later we can replay these events, this is called Event Sourcing, there are some EventSoucing database (functional database/Event Store, such as GETEventStore). Domain Event have Producer-Consumer(1:1) and Publisher-Subscriber(1:N) patterns: Domain Model is a producer; it send events or message to Consumer: @Send(topicName) => @Consumer(topicName); if there are many consumers, their started order is by their class name's alphabetical . such AEventHandler is first be reaction; and then BEventHandler and CEve....... How to use Domain Events
When you fetch a MyModel instance from service or repository, because of @Inject, CacheInterceptor or DomainCacheInterceptor will inject MyModelDomainEvent into the MyModel instance,When injecting the MyModelDomainEvent, jdon find MyModelDomainEvent class has annotation with @Introduce("message") , then will at first load MessageInterceptor(in aspect.xml message=MessageInterceptor),and last return a proxy instance . When we call myModel proxy instance, such as invoke its method "myModel.getName()", the MessageListenner will intercept our invocation,it will choose proper means to drive the MessageListner to response invocation request. full domain events example is in here:DDD CQRS EventSourcing example source: football Match wo can use domain events to model the requirement, see CES:Context Event and State and Model Storming
Producer/ConsumerJdon provide four kinds of asynchronous concurrency communication(Producer/Consumer) with these two models. 1. Component -----> model (that is a Command) 2. model ------->Component (that is a event) 3. Compponent ------> Component (that is event) 4. model------> model (commands and events) here is Summary: 1. Component => ModelComponent(producer with @Component) -- > Model(consumer with @Model) this mode is for CQRS's Command, one command is sent from UI, and sent to one aggregate root, one command has one action on the method of the aggregate root. this mode works. in this mode producer:consumer can only be 1:1, one command can only be sent to one method of one domain model. The A in this sample is a producer with @Component:
the method "ma" annotated with @Send must have a method parameter:BModel that annotated with @Model, that means send a command message to this domain model , the model is a receiver for a command, so must annotate with @Receiver.
the topic name "CommandmaTest" is unique; because producer : consumer is 1:1. one command has one action(onCommand). @OnCommand is annotated on the method of consumer. the procuder(AICommand) 's ma method commands the consumer(BModel)'s save method. AICommand is a Component and BModel is a Model. below is the client code:
it output: send to BModel =oneok 5 above source in :https://github.com/banq/jdonframework/tree/master/src/test/java/com/jdon/sample/test/command in this mode the component receives any commands from UI or others events, and handles them to a aggregate root : UI ------->commandHandler(@Component) ------>aggregate root model in this mode, @Model is a actor and representing Aggregate Root , the state of aggregate root is changed by only single thread, it is threadsafe. and no blocking and no lock, like Actors mode of AKKA or ERlang, or like Node.js EDA. instead of 2PC or JTA, this mode can make transaction work well in high throughput way. When we apply Jdon in a web application, the system works something like this:
2.Model => ComponentModel(producer with @Model) -- > Component(consumer with @Component) After a agrregate root (domain model) receive a command , and it will reactive a event that can be sent to a Component or another aggregate root. we call it is domain events. we can inject a Component(with @Component) into a agrregate root that acts as a producer. the domain model code:
we inject MyModelDomainEvent into "MyModel" with @Inject, the injected object "MyModelDomainEvent" is a Component:
MyModelDomainEvent is annotated with @Introduce("message"), in this producer class there are two topic that means two kinds of domain events. let's see how to implement a consumer for a domain event. the consumer is too a Component, see below:
FindNameListener is annotated with @Consumer, not @Component, this is another consumer style, when using @Consumer, we need implement a interface DomainEventHandler, and implement its method onEvent, if we using @Component, we need use @OnEvent together. above consumer is for the producer @Send("MyModel.findName"), another consumer for this producer @Send("saveMyModel") of this sample uses @Component and @OnEvent:
this consumer is a class RepositoryImp that it is a Repository of DDD, and it also fetch a aggregate root from repository, @Introduce("modelCache") and @Around enable in-memory cache before the database. only when there is no the model object in cache, it will got from the database. @Introduce("modelCache") and @Around is necessary for using jdon framework. if not use them, any domain events will disable. another way,we can call com.jdon.domain.dci.RoleAssigner assignAggregateRoot method to let any normal object be act as a aggregate root. the in-memory cache make Aggregate Root living in memory, you can configure any cache product(ehcache, Redis ) for it. ensure 'hot' entities are kept in memory for performance. we have known about two interactive modes between Component and aggregate root, if using these two modes in a aggregate root, it can be regarded as a Actors mode like Akka or Erlang:
above sources: https://github.com/banq/jdonframework/tree/master/src/test/java/com/jdon/sample/test/domain/simplecase
3.Component => ComponentComponent(producer with @Component) -- > Component(consumer with @Component) in this mode there are two ways: 1. dependency inject 2. producer and consumer based on Event. Dependency inject is like IOC, Jdon only supports class construtor inject:
after B was injected into A, A can directly invoke any B's method. another way is more loose couple,between A and B there are communicates through asynchronous& non-blocking message passing . The A in this sample is a producer with @Component:
The B in this sample is a consumer with @Component, and its consumer method must be annotated with @OnEvent,
the value "maTest" of @OnEvent must be same as the value of @Send. and the return type "TestEvent" of the method with @Send must be same as the input paramter type of the @OnEvent, so the producer can handle a object to the consumer. if the consumer want to return a result to the producer, recommend to use method setEventResult of com.jdon.domain.message.DomainMessage, and the producer can fetch the result by its getBlockEventResult() or getEventResult(), getBlockEventResult() can block until get the result.
Test client code:
output: [junit] event.send.ma..event.@OnEvent.mb..100 above codes is fromhttps://github.com/banq/jdonframework/tree/master/src/test/java/com/jdon/sample/test/event
4.Model => ModelModel(aggregate root A) -- > Model(aggregate root B) Aggregates
are
always
internally
consistent, and Aggregates"eventually
consistent" with
each
other.
– Asynchronous when a aggregates root want to cal another aggregates root , it only can be implemented by domain events, a output event act as a input command for another aggregates root. this mode consists of three modes above: 1. Aggrgate root A reactive a event to a Component (model ---> Component) 2.the Component transfer the event to a command for Aggrgate root B (Component ---->Component) 3.the command will be sent to Aggrgate root B (component -->model) this mode's source code is in Github: https://github.com/banq/jdonframework/tree/master/src/test/java/com/jdon/sample/test/cqrs
DCIDCI: Data, Context, Interactions is a programming paradigm invented by Trygve Reenskaug. the domain model is a Data, the domain events producer is a Role of DCI, it includes the interactions of DCI. Jdon Framework's domain model injection function can inject the role into the domain model, such as above the class UserDomainEvents is a Role, it can be injected into the model UserModel.so it help UserModel implements business function ComputeCount. In runtime, The Role UserDomainEvents will be injected into the Data UserModel.
AOPUsing the DDD,It is the domain models drive the technical components to finish the business logics,and in this design style,the components are just the assistants,which assist the domain models to complete the business operations.So how the domain models gain the needed components,Jdon framework use the @Inject and @Component to inject components and the domain events into models. As the followed diagram illustrates ,when client invoke the getA("a"),if Model A with a Introduce,the A will be enhanced,and the technical component C will also be injected into the A ,at the same time,when inject the B,the B will also be enhanced for the @Introduce.
So your mycontainer.xml file and myaspect.xml file must be put in WEB_INF directory in your web application, yet file names could be arbitrary. In User-defined configuration file, corresponding settings in container.xml and aspect.xml could be overwritten; however the setting name should be the same.
How to integrate Jdon Framework with Spring
Ioc container
The great thing about Jdon is it helps you replace them((even you can replace jdon itself!). All framework components include framework infrastructure objects are be managed by a micocontainer made by Picocontainer, you can insert or change any components by change the xml configuration files(such as container.xml/aspect.xml/jdonframework.xml), you can also replace Jdon framework's infrastructure components(this is a strongpoint). Jdon framework will make your components. the infrastructure components or POJO services collaborate with each other via dependency injection. When you have hundreds of components/POJO services, Jdon's autowiring function will take care of their invocation relation. Interceptor : Service/Componet: XML : in jdonframework.xml (container.xml) there is such service configuration:
in code, we can get the service instance by below mode:
Jdon defines four service models: component instance that is singleton, service instance that can be pooled about Choice with Annotaion and XML in Ioc container:XML in IoC containers: A Hell or A Realm? Service Annotaion @Poolable: get a instance from the class's object pooing, Object Pooling - Determinism vs. Throughput RAD Tool(jdon-struts1x) Keep deliberately simple, yet still very powerful, so that your code is kept simple. Jdon provides a fast development way for model's CRUD(create/read/update/delete) , in presentation layer, in general, you don't need any code, only configure MVC Process in the jdonframework.xml:
Jdon's presentation layer is based Struts 1.2, so Jdon will generate Action instance at running time. Multi page query function can be accomplished quickly by Jdon, Jdon provide the multi-page taglib, JDBC template, and Model's cache optimization. Use Jdon, you can develop a small system in several minutes, here is the system demo:http://www.jdon.com:8080/testWeb/ Step by Step develop a Jdon application with jdon-struts1x
Componentsneed two annotations(@Service or @Component):difference between @Service and @Component is that the @Service class must have a interface then can be called by outside client beyond jdon.
the client in servlet or action call the service's code: HelloService helloService = (HelloService) WebAppUtil.getService("helloService", req); all source in JdonFramework/examples/testWeb More Annotation @Poolable: get a instance from the class's object pooing, Object Pooling - Determinism vs. Throughput
XML configurationWe called a class instance invoked by client (Servlet/Jsp/Action) as below.
"testService" is the class name of "TestService". it should be configured in jdonframework.xml
Not all of classes are servered for the client directly (jsp/Servlet), that is to say some classes are not invoked by jsp/servlet or presentation frame layer, but invoked by service classes, these kinds of class we called component class annotated as component in configuration file. Note: in EJB framework, component class is managed by EJB container, so you do not need to any configuration in JdonFramework any more.
<pojoService name="your service name " class="full class "/> or @Service("your service name") "class" is the class full name, include the package; "name" can be any text that will be used in our client code. if we have a class TestServicePOJOImp as below:
when we finished these codes, we need create a XML file that named jdonframework.xml under project source directotry:
at last, if we invoke TestServicePOJOImp in client code such as Jsp/Servlet or struts action,the invoking code as below:
so the good point is that: if we replace TestServicePOJOImp with AnotherTestServicePOJOImp , only need modify jdonframework.xml, not need change our client codes. thera is another big good point that Ioc or DI means Dependency Injection, maybe you have find a details about TestServicePOJOImp that it has a constructor method: public TestServicePOJOImp(JdbcDAO jdbcDao) { if we donot create the JdbcDAO instance, how can we create TestServicePOJOImp instance?don't worry, the framework help us and it do these, but we need add a new configuration item in jdonframework.xml, as below:
until now, you maybe have understand JdonFramework what to do.
How starting the framework?If there is no xml, all are annotation, no need this step. there are two ways to starting the framework in our web project. first way: web.xml configuration:
second way: struts-config.xml(you must know struts 1.2)
if not used in Web , can be used in java Application, see below: ClientClient not only Servlet or Jsp or other Web Framework,:
or
but also can be a Java Application.
Source :com.jdon.SampleAppTest
How to compile and deployMaven pom.xml: <dependency> if you would like to use jdon's CRUD function based struts1.x(such as jdonframework.xml), add :
if you would like to use Jdon's jdbcTemp, add:
full pom.xml
jdon old version :How to develop Struts-Jdon-JDBC Application Command patern for calling services Struts + Jdon + Hibernate video
BlogDDD DCI and Domain Events examplehow to enhance Jdon framework to DCI framework ?Immutability is everythinghow to easily develop a non-blocking concurrent application ?
|