vaughnvernon / iddd_samples Goto Github PK
View Code? Open in Web Editor NEWThese are the sample Bounded Contexts from the book "Implementing Domain-Driven Design" by Vaughn Vernon: http://vaughnvernon.co/?page_id=168
License: Other
These are the sample Bounded Contexts from the book "Implementing Domain-Driven Design" by Vaughn Vernon: http://vaughnvernon.co/?page_id=168
License: Other
Hi Sir, where is the aggregate root in in this project?
File:
Error:
org.hibernate.InvalidMappingException: Could not parse mapping document from resource StoredEvent.hbm.xml
How do I open this project in java? Eclipse or Netbeans?
Hi
Vaughn, could you explain it? I'd appreciate ;]
For example Product: uses ProductId and TentatId.
public class Product extends Entity {
private Set<ProductBacklogItem> backlogItems;
private String description;
private ProductDiscussion discussion;
private String discussionInitiationId;
private String name;
private ProductId productId;
private ProductOwnerId productOwnerId;
private TenantId tenantId;
// ..
@Override
public boolean equals(Object anObject) {
boolean equalObjects = false;
if (anObject != null && this.getClass() == anObject.getClass()) {
Product typedObject = (Product) anObject;
equalObjects =
this.tenantId().equals(typedObject.tenantId()) &&
this.productId().equals(typedObject.productId());
}
return equalObjects;
}
}
Entity is identified by its ID (ProductId) thus:
Thank you
GT
BTW. Great book. I'm reading it 2nd time ;]
I tried setting FOR_MYSQL
to true and FOR_LEVELDB
to false but the test would not proceed because of null exception.
If you trace these lines:
The instance is not initialized.
I tried putting mysqlJdbcEventStore
before followStoreEventDispatcher
in applicationContext-collaboration.xml
so that the mysql is initialized first but it's not working also.
I am not a java guy so I dont actually know what's happening.
I would like to try MySQL instead of LevelDB.
when the user doesn't have the role, method like authorFrom will return null but I cant see any code dealing with this situation? or this sample just assume that the user always have the role?thanks for answing
Inside ProductApplicationService
there appears to be a transaction boundary, culminated with `ApplicationServiceLifeCycle.success().
It wraps calls to multiple repositories, which, to my understanding, saves changes made in two distinct aggregate roots:
I want to make sure that I'm not missing any important context here. As per Vernon's own words:
A properly designed aggregate is one that can be modified in any way required by the business with its invariants completely consistent within a single transaction. And a properly designed bounded context modifies only one aggregate instance per transaction in all cases.
Is this an example of a situation where the rule of thumb does not apply? Does anyone have a better explanation?
I have a doubt about the code organization. As an example, the com.saasovation.identityaccess.infrastructure.persistence
package contains classes that implement repositories using hibernate. But a similar purpose is served by the com.saasovation.agilepm.port.adapter.persistence
package in the other context which was implemented with LevelDB.
One possible explanation that I see is that hibernate is considered the adapter and that's why there is no need for implementing a package com.saasovation.identityaccess.port.adapter.persistence
. However, in the agilepm context, there is no infrastructure layer at all and only the port & adapter layer. Is that because, for example, com.saasovation.agilepm.port.adapter.persistence.LevelDBSprintRepository
fulfills the role of infrastructure and adapter at the same time and you chose to to not separate those concerns?
Another doubt is that the endpoints declared in the com.saasovation.identityaccess.resource
package are at the "top level" of that context. I would have expected them to be in port & adapters.
Thank you for any help to clear up my confusion.
i'm really interested in seeing the sample hibernate configuration files for the identityaccess BC, but I can't seem to locate them. Are they in the project somewhere?
Hi Vaughn.
A discussion can not be started on closed forum.
Isn't it the "true invariant" and then mustn't Discussion be a part of Forum aggregate?
And if it's really not, can you please clarify?
Thanks a lot.
I found the various listeners like this but I don't understand how they are instantiated. I did not found them in IoC configuration. How are they instantiated ?
The class com.saasovation.agilepm.port.adapter.messaging.rabbitmq.RabbitMQTeamMemberEmailAddressChangedListener
is listening to the event com.saasovation.identityaccess.domain.model.identity.PersonContactInformationChanged
which is fired from the iddd_identityaccess
module.
I think it's an implicit dependency between the two modules that actually make this two modules coupled
I am not sure if there are any better approach to eliminate this kind of implicit dependency
Hi,
while reading the book i stumbled around ValidationNotificationHandler.
Something that is missing both book and repo is the actual implementation of ValidationNotificationHandler.
What should be done inside handleError(String aNotificationMessage) ?
Thank you all.
When I run gradle clean build
test cases fail with:
java.lang.IllegalStateException: Cannot open LevelDB database: /data/leveldb/iddd_agilepm_db because: Database directory '/data/leveldb/iddd_agilepm_db' does not exist and could not be created
at com.saasovation.common.port.adapter.persistence.leveldb.LevelDBProvider.openDatabase(LevelDBProvider.java:140)
Hello, Mr VernonI.
I read the article "The Ideal Domain-Driven Design Aggregate Store?" at https://kalele.io/the-ideal-domain-driven-design-aggregate-store/
Now mongodb already supports ACID transactions (after 2018 summer).I wonder whether mongodb is capable of storing ddd in production environment or not.
I'm worried about performance.
https://docs.mongodb.com/master/core/transactions/ :
IMPORTANT
In most cases, multi-document transaction incurs a greater performance cost over single document writes, and the availability of multi-document transactions should not be a replacement for effective schema design.
https://www.mongodb.com/blog/post/multi-document-transactions
With subdocuments and arrays, document databases allow related data to be unified hierarchically inside a single data structure. The document can be updated with an atomic operation, giving it the same data integrity guarantees as a multi-table transaction in a relational database.
Because of this fundamental difference in data modeling, MongoDB’s existing atomicity guarantees are able to meet the data integrity needs of most applications. In fact, we estimate 80%-90% of applications don’t need multi-document transactions at all.
As you can see in this two articles written by mongodb official, the ACID transaction of mongodb is probably designed for some corner cases (probably 10% of 10% projects, I guess). But in DDD we use multi-document transactions frequently. ACID transaction is needed whenever we want to publish or handle a domain event.
So I'm worried that the way they design mongodb makes the transaction of mongodb not friendly with Domain Driven Design. But I cannot find benchmarks or some other articles about this topic. Would you please answer my question?
In addition, in https://kalele.io/the-ideal-domain-driven-design-aggregate-store/ , you said that postgres is very fast. I would like to know which one should be chosen to store aggregates (1) when db runs on single machine (2) when db is distributed
I have already read some articles about the this topic including this one from @VaughnVernon and I'm almost done with the book IDDD but I'm still not clear on some facts so sorry to bring it up here again.
To sum up what I already know I just want to go throw the facts here. It is clear that in almost all the cases, authorization is an integrated part of the domain model. For example only an author is allowed to post to a forum. It is also clear that authentication is NOT part of any bounded context but the ones that are directly related to this matter (i.e. identity and access here).
However there are some edge cases that still keep me wondering if there is an answer to them in DDD style to them or do they need a combination of other architecture with DDD.
Lets take the tenant provisioning as an example. How do we answer the question "who is authorised to provision a tenant?". Shall the actor here be part of scenario (like author in forum example) or is there a God-mode way that something happens without an actor involved? if so how we authorise it?
Or take the use case where "a moderator is allowed to modify a post on behalf of its author?". Is this a use case that we still need to model in current code? or is this the same use case and same service call where author himself modifies the post only with an extra check that if the request is done by a moderator who has proper role?
Another question is how do we verify the genuinity of the requester. We can't only depend on tenant and author's id as these could be part of public data that we freely show to everybody. For example as a link to author's profile on a forum post. So we need a secret (like password) to do the verification. Will this secret be part of the command sent to relevant application service? or shall it be done before the request reaches the application service? like inside JAXRS resource by calling directly the UserRepository.
Again sorry to bother but these are the things I can't just shake off my head.
The method c.s.common.notification.NotificationLogFactory.calculateCurrentNotificationLogId
computes an erroneous NotificationLogId
when the event store is empty. For example NOTIFICATIONS_PER_LOG
is set to 20. Then at line 61, 0 % 20 = 0. Then at line 64 remainder
is set to 20. Finally, at line 67, low
is set to 0 - 20 + 1 = 19.
By changing the condition at line 63 into remainder == 0 && count > 0
, the issue is fixed.
startContainers.sh
use hostname as RABBITMQ_NODENAME.
rabbitmqNodeName="$(hostname)"
...
docker run --name "${rabbitmqContainerName}" -p 5672:5672 -p "${rabbitmqManagementHttpPort}":15672
-e RABBITMQ_NODENAME="${rabbitmqNodeName}" -d rabbitmq:3-management
Dots are not available as RABBITMQ_NODENAME.
but, on Mac, $(hostname) is like "chytonpide_mac.local" as a default.
(There is a very high probability that the hostname contains a dot, even if it is a different system.)
I think it is unnecessary to use the "RABBITMQ_NODENAME" environment variable.
So I suggest deleting the "RABBITMQ_NODENAME" environment variable on the line that runs the RabbitMQ container or using a static node name that does not include dots.
As soon as the container is ready, SQLs are executed. But, executions fail because the Mysql server is not prepared even if the container is ready.
I suggest having a delay after the container is ready.
waitForContainer "${mysqlContainerName}" "mysqld: ready for connections."
sleep 30
Not the best solution, but it works and is simple :)
I suggest replacing the setup script with docker-compose.
I think it makes the setup process easier to maintain. it also could be used in Windows systems.
The docker-compose is included in Docker for Mac and Docker for Windows installer.
So there is no need for other installation processes for this.
What's the point of having different packages com.saasovation.identityaccess.application
and com.saasovation.identityaccess.application.command
?
AssignUserToRoleCommand
, you use AccessApplicationService
. If you use AccessApplicationService
, you use AssignUserToRoleCommand
. If classes are used together, it makes sense for them to be in the same package. The Common Reuse Principle: "Classes that are used together are packaged together". See http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod.com.saasovation.identityaccess.domain.model
services and other types of classes (entities, ...) are in the same packages. Why should it be different inside com.saasovation.identityaccess.application
?A duplicated tracker is created when retrying ProductDiscussionRequest.
ProductApplicationService.retryProductDiscussionRequest()
calls Product.requestDiscussion()
.Product.requestDiscussion()
publish ProductDiscussionRequested
event.ProductApplicationService.startDiscussionInitiation()
.and then, the ProductApplicationService.startDiscussionInitiation()
creates the tracker again even though a retry has been requested by an existing tracker.
I think it is needed to add the logic that creating a tracker only when the Product
doesn't have discussionInitiation
.
public void startDiscussionInitiation(StartDiscussionInitiationCommand aCommand) {
...
if(product.discussionInitiationId() == null) {
...create new tracker
}
...
}
Can anyone please tell me the purpose of ThreadLocal in DomainEventPublisher? Does that mean every thread needs to maintain a list of its own subsriber list?
Hello! I really enjoyed the book and the papers. I have a question. As you also mentioned in the Effective Aggregate Design Part I: Modeling a Single Aggregate paper, a rule of thumb says that we should be limiting the modification of one aggregate per transaction. I am wondering why though? How does this rule of thumb help us? Why not persist two aggregates in the database under the same transaction? Thanks!
I'm trying to understand the eventing and am a bit puzzled cause when I follow the invocations starting from RabbitMQBacklogItemCommitedListener, it calls SprintApplicationService, which calls sprint.commit(BacklogItem bli). It doesn't publish any event.
Then on the BacklogItem entity I also see a "commitTo" method, which does publish an event, but this method is only called from the tests.
That looks like something is wrong/inconsistent here...
Can anyone explain why IdentifiedValueObject has identity? Does not it violate definition of Value Object? Is it domain identity or persistence identity?
Hi Vaughn,
I've been searching for the database schema of agilepm. The other context have .sql files.
Where can I find it?
Hello,
there is a setter for concurrencyVersion in ConcurrencySafeEntity. But this setter is not called from anywhere.
How is it supposed to be used? Shouldn't be the concurrencyVersion contained in every command send to the entity to submit the version the client has seen when reading the aggregat in the first place?
If so, what would be the best place to call setConcurrencyVersion? In the application service (or even in the repository) after loading the aggregate or in the entities' methods?
Thanks + Best Regards,
Stefan.
How different would be to change this reference project towards one embracing functional programming? Does it make sense rich domain model and event publishing from domain if we use extensively streams from Java or kotlin?
Maven central discontinued support for TLSv1.1 and below.
Gradle v2.3 with Java7 used in the project use TLSv1.1
so it is needed to use Gradle version 4.8.1 and above or Java8 and above that supports TLSv1.2.
It is needed to use a compatible version of MySQL with mysql-connector-java.
collaboration.sql
needs to be changed from varchar(65000)
to text
or specify charset as latin1 when creating DB because MySQL v8.0.28 default database charset is utf8mb4.AbstractQueryService
needs to be changed to use ResultSet.TYPE_SCROLL_SENSITIVE
as resultSetType.Every combination of available options passed all tests except two SlotMQ tests.
The combination of using Java7, MySQL v5.7, and Gradle v4.8.1 is the minimum change.
Using Java8 needs to change the Spring dependency of the version to v3.2.18.
Using MySQL v8.0.28 needs to change "collaboration.sql" and AbscractQueryService.
Hi,
In the book, it's clearly stated that domain services should be stateless. However, we noticed that BusinessPriorityCalculator (and possibly other services) do store a repository reference.
We can see why this may not be an issue in practice, given that BacklogItemRepository should be a singleton, but we don't know if that's the reason why this exception was made.
What rules of thumb do you use when sidestepping the "services should be stateless" rule? Thanks!
I have discovered an insidious bug in NotificationLogFactory.calculateCurrentNotificationLogId
.
Indeed, for this to work, we assume that stored event ids are generated by the database and that they is never any gap between them. However this is not a valid assumption as indicated in the MySQL documentation:
In all lock modes (0, 1, and 2), if a transaction that generated auto-increment values rolls back, those auto-increment values are “lost.” Once a value is generated for an auto-increment column, it cannot be rolled back, whether or not the “INSERT-like” statement is completed, and whether or not the containing transaction is rolled back. Such lost values are not reused. Thus, there may be gaps in the values stored in an AUTO_INCREMENT column of a table.
Consider this example: we have 20 stored events, the first identified by 1 and the last by 21. Because at some point we had to rollback, the generated id 3 is lost. So the stored events have the following ids: 1, 2, 4, 5,... up to 21. Also we have NOTIFICATIONS_PER_LOG = 20
.
Now in the calculateCurrentNotificationLogId
method the variables take the following values:
count = 20
remainder = count % NOTIFICATIONS_PER_LOG = 0
// since remainder == 0 && count > 0
remainder = NOTIFICATIONS_PER_LOG = 20
low = count - remainder + 1 = 20 - 20 + 1 = 1
high = low + NOTIFICATIONS_PER_LOG - 1 = 20
So the current notificationLogId is [1-20], which will return the stored events between ids 1 and 20. So this log will only contain 19 notifications and the last stored event will be ignored.
I have found two solutions to fix this problem:
lastStoredEventId()
method to EventStore
and replace the calls to countStoredEvents()
with lastStoredEventId()
.This way we don't use the number of stored events to calculate the current notification log and we don't skip any stored event anymore. However we have to accept that not all archived notification logs will contain exactly NOTIFICATIONS_PER_LOG
notifications.
Note that in some extreme cases (with many rollbacks), we may even have empty notification logs. But as long as we don't rely on the exact number of notifications per log when we consume them, I think this solution is fine. This is what I have implemented in my company.
countStoredEvents()
method, but replace the allStoredEventsBetween(long aLowStoredEventId, long aHighStoredEventId)
by something like allStoredEventsBetween(long aLowIndex, long aHighIndex)
or allStoredEvents(long anOffset, long aLimit)
.This way we don't rely on the stored event ids and we have no gap between the notification ids, i.e., the archived notification logs will always contain NOTIFICATIONS_PER_LOG
notifications.
However for any given notification, its id will not always match the id of its encapsulated event. On one hand we can argue that this is fine, since it doesn't expose how stored events are identified internally. On the other hand, for debugging or other purposes, we may like the fact that a notification id reflects the stored event id.
So what do you think about that?
Sorry for the dump question. When I read the code in iddd_agilepm module, I don't find out any services to retrieve data to display. I guess that it is CQRS and just show only command (update database), so it ignores query model.
so, it may have two options:
Build dedicated query model
Add query functions in the services. If do that, I found problem of small aggregates. Because of separating big grasp of object by join sql query into small ones with multiple single-table sql query that is more slower.
thanks for your reading, hope you can answer me
Hi Vaughn.
First of all, thank u for contributions in computer science world.
While I was reading ur example, I asked myself about the domain models. I think that constructors have a lot of responsabilities, since they call set methods in which it validates and throw exceptions if it violates any domain rule. Why do u use constructors and not another pattern? Maybe a SFM (static factory method) or Builder can help to keep constructors simpler.
Thanks in advance.
Hi,
We are preparing for the IDDD_Workshop in Paris and are getting the following error when running gradle build
. We are not so familiar with Java (understatement), so I have no clue if this has to do with our Java version, or some other settings.
I am working on a MacBook Air with Java 1.6.0_65. We used brew install maven
and brew install gradle
to install gradle on our machines, so I suppose we have the latest version. Anyone a clue?
.../IDDD_Samples/iddd_common/src/main/java/com/saasovation/common/port/adapter/messaging/slothmq/SlothWorker.java:152: cannot find symbol symbol : method bind(java.net.InetSocketAddress) location: class java.nio.channels.ServerSocketChannel this.socket.bind(new InetSocketAddress(discoveryPort)); ^ .../IDDD_Samples/iddd_common/src/main/java/com/saasovation/common/port/adapter/messaging/slothmq/SlothWorker.java:191: cannot find symbol symbol : method bind(java.net.InetSocketAddress) location: class java.nio.channels.ServerSocketChannel this.socket.bind(new InetSocketAddress(HUB_PORT)); ^ .../IDDD_Samples/iddd_common/src/main/java/com/saasovation/common/port/adapter/persistence/ResultSetObjectMapper.java:227: cannot find symbol symbol : method isAlphabetic(char) location: class java.lang.Character if (Character.isAlphabetic(ch) && Character.isUpperCase(ch)) { ^ 3 errors :iddd_common:compileJava FAILED
hi,
I'm trying to compile this (using Java 7), using the gradlew build
command. it ends up with peer not authenticated errors:
...
:iddd_common:compileJava`
FAILURE: Build failed with an exception.
* What went wrong:
Could not resolve all dependencies for configuration ':iddd_common:compile'.
> Could not resolve org.slf4j:slf4j-api:1.5.8.
Required by:
IDDD_Samples:iddd_common:unspecified
> Could not GET 'https://repo1.maven.org/maven2/org/slf4j/slf4j-api/1.5.8/slf4j-api-1.5.8.pom'.
> peer not authenticated
> Could not GET 'https://repository.jboss.org/nexus/content/groups/public-jboss/org/slf4j/slf4j-api/1.5.8/slf4j-api-1.5.8.pom'.
> peer not authenticated
> Could not resolve commons-logging:commons-logging:1.1.2.
Required by:
IDDD_Samples:iddd_common:unspecified
> Could not GET 'https://repo1.maven.org/maven2/commons-logging/commons-logging/1.1.2/commons-logging-1.1.2.pom'.
> peer not authenticated
> Could not GET 'https://repository.jboss.org/nexus/content/groups/public-jboss/commons-logging/commons-logging/1.1.2/commons-logging-1.1.2.pom'.
> peer not authenticated
> Could not resolve com.google.code.gson:gson:2.1.
Required by:
IDDD_Samples:iddd_common:unspecified
> Could not GET 'https://repo1.maven.org/maven2/com/google/code/gson/gson/2.1/gson-2.1.pom'.
> peer not authenticated
> Could not GET 'https://repository.jboss.org/nexus/content/groups/public-jboss/com/google/code/gson/gson/2.1/gson-2.1.pom'.
> peer not authenticated
> Could not resolve com.rabbitmq:amqp-client:3.0.4.
Required by:
IDDD_Samples:iddd_common:unspecified
> Could not GET 'https://repo1.maven.org/maven2/com/rabbitmq/amqp-client/3.0.4/amqp-client-3.0.4.pom'.
> peer not authenticated
> Could not GET 'https://repository.jboss.org/nexus/content/groups/public-jboss/com/rabbitmq/amqp-client/3.0.4/amqp-client-3.0.4.pom'.
> peer not authenticated
> Could not resolve org.hibernate:hibernate:3.2.7.ga.
Required by:
IDDD_Samples:iddd_common:unspecified
> Could not GET 'https://repo1.maven.org/maven2/org/hibernate/hibernate/3.2.7.ga/hibernate-3.2.7.ga.pom'.
> peer not authenticated
> Could not GET 'https://repository.jboss.org/nexus/content/groups/public-jboss/org/hibernate/hibernate/3.2.7.ga/hibernate-3.2.7.ga.pom'.
> peer not authenticated
> Could not resolve org.springframework:spring:2.5.6.
Required by:
IDDD_Samples:iddd_common:unspecified
> Could not GET 'https://repo1.maven.org/maven2/org/springframework/spring/2.5.6/spring-2.5.6.pom'.
> peer not authenticated
> Could not GET 'https://repository.jboss.org/nexus/content/groups/public-jboss/org/springframework/spring/2.5.6/spring-2.5.6.pom'.
> peer not authenticated
> Could not resolve org.iq80.leveldb:leveldb:0.5.
Required by:
IDDD_Samples:iddd_common:unspecified
> Could not GET 'https://repo1.maven.org/maven2/org/iq80/leveldb/leveldb/0.5/leveldb-0.5.pom'.
> peer not authenticated
> Could not GET 'https://repository.jboss.org/nexus/content/groups/public-jboss/org/iq80/leveldb/leveldb/0.5/leveldb-0.5.pom'.
> peer not authenticated
> Could not resolve org.aspectj:aspectjweaver:1.7.2.
Required by:
IDDD_Samples:iddd_common:unspecified
> Could not GET 'https://repo1.maven.org/maven2/org/aspectj/aspectjweaver/1.7.2/aspectjweaver-1.7.2.pom'.
> peer not authenticated
> Could not GET 'https://repository.jboss.org/nexus/content/groups/public-jboss/org/aspectj/aspectjweaver/1.7.2/aspectjweaver-1.7.2.pom'.
> peer not authenticated
> Could not resolve javassist:javassist:3.8.0.GA.
Required by:
IDDD_Samples:iddd_common:unspecified
> Could not GET 'https://repo1.maven.org/maven2/javassist/javassist/3.8.0.GA/javassist-3.8.0.GA.pom'.
> peer not authenticated
> Could not GET 'https://repository.jboss.org/nexus/content/groups/public-jboss/javassist/javassist/3.8.0.GA/javassist-3.8.0.GA.pom'.
> peer not authenticated
> Could not resolve javax.transaction:jta:1.1.
Required by:
IDDD_Samples:iddd_common:unspecified
> Could not GET 'https://repo1.maven.org/maven2/javax/transaction/jta/1.1/jta-1.1.pom'.
> peer not authenticated
> Could not GET 'https://repository.jboss.org/nexus/content/groups/public-jboss/javax/transaction/jta/1.1/jta-1.1.pom'.
> peer not authenticated
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
i've tried to inject "-Dmaven.wagon.http.ssl.insecure=true"
and "-Dmaven.wagon.http.ssl.allowall=true"
into the Java command itself but to no effect unfortunately. would you have any recommendation on how to go about fixing this?
tks.
I'm running Windows 10, latest build as of 10/31, and java version
java version "1.7.0_80"
Java(TM) SE Runtime Environment (build 1.7.0_80-b15)\
Java HotSpot(TM) 64-Bit Server VM (build 24.80-b11, mixed mode)
Hello,
According to the code, it's possible to post on a closed discussion. I am missing something ?
We can easily fix it by adding an assertion on Discussion.post would fix the problem.
But what about posting on a opened discussion that belongs to a closed forum ?
Does moving post method from Discussion to Forum be a good solution ?
Thank you
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.