├── .gitignore ├── LICENSE.txt ├── README.md ├── pom.xml └── src ├── main └── java │ └── co │ └── vaughnvernon │ └── mockroservices │ ├── exchange │ └── InformationExchangeReader.java │ ├── fn │ ├── Tuple2.java │ └── Tuple3.java │ ├── journal │ ├── EntryBatch.java │ ├── EntryStream.java │ ├── EntryStreamReader.java │ ├── EntryValue.java │ ├── Journal.java │ ├── JournalPublisher.java │ ├── JournalReader.java │ ├── Repository.java │ ├── StoredSource.java │ └── StreamNameBuilder.java │ ├── kvstore │ └── KVStore.java │ ├── messagebus │ ├── Message.java │ ├── MessageBus.java │ ├── MessageExchangeReader.java │ ├── Subscriber.java │ └── Topic.java │ ├── model │ ├── Command.java │ ├── DomainEvent.java │ ├── Id.java │ ├── ProcessManager.java │ ├── SourceType.java │ └── SourcedEntity.java │ └── serialization │ └── Serialization.java └── test └── java └── co └── vaughnvernon ├── SomeEventTypeName.java ├── SomeOtherEventTypeName.java └── mockroservices ├── Person.java ├── Product.java ├── journal ├── IntegrationTest.java ├── JournalPublisherTest.java ├── JournalTest.java ├── ProductRepository.java ├── RepositoryTest.java ├── SomeEventTypeName.java └── StreamNameBuilderTest.java ├── kvstore └── KVStoreTest.java ├── messagebus ├── MessageBusTest.java └── MessageExchangeReaderTest.java ├── model ├── EventSourcedRootEntityTest.java └── TestableDomainEvent.java └── serialization └── SerializationTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | target/ 3 | .metadata/ 4 | .classpath 5 | .project 6 | .settings 7 | .DS_Store 8 | *.log 9 | /.idea/ 10 | /mockroservices.iml 11 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaughnVernon/mockroservices-java/137ab500d76653cc83ff13db1fcf7e2090d5c6ac/LICENSE.txt -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mockroservices-java 2 | Toolkit for creating microservices in a multi-project test environment using mock distributed mechanisms. 3 | 4 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | co.vaughnvernon 4 | mockroservices 5 | 1.2.0 6 | 7 | 1.8 8 | 1.8 9 | 10 | 11 | 12 | junit 13 | junit 14 | 4.13.1 15 | test 16 | 17 | 18 | com.google.code.gson 19 | gson 20 | 2.8.9 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/main/java/co/vaughnvernon/mockroservices/exchange/InformationExchangeReader.java: -------------------------------------------------------------------------------- 1 | // Copyright � 2017-2022 Vaughn Vernon. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package co.vaughnvernon.mockroservices.exchange; 16 | 17 | import java.math.BigDecimal; 18 | import java.util.Date; 19 | 20 | import com.google.gson.JsonArray; 21 | import com.google.gson.JsonElement; 22 | import com.google.gson.JsonObject; 23 | import com.google.gson.JsonParser; 24 | 25 | public abstract class InformationExchangeReader { 26 | private JsonObject representation; 27 | 28 | public InformationExchangeReader(final String jsonRepresentation) { 29 | representation = parse(jsonRepresentation); 30 | } 31 | 32 | public InformationExchangeReader(final JsonObject jsonRepresentation) { 33 | this.representation = jsonRepresentation; 34 | } 35 | 36 | public JsonArray array(final String... keys) { 37 | JsonArray array = null; 38 | 39 | JsonElement element = navigateTo(representation(), keys); 40 | 41 | if (element != null) { 42 | array = element.getAsJsonArray(); 43 | } 44 | 45 | return array; 46 | } 47 | 48 | public BigDecimal bigDecimalValue(final String... keys) { 49 | final String stringValue = stringValue(keys); 50 | 51 | return stringValue == null ? null : new BigDecimal(stringValue); 52 | } 53 | 54 | public Boolean booleanValue(final String... keys) { 55 | final String stringValue = stringValue(keys); 56 | 57 | return stringValue == null ? null : Boolean.parseBoolean(stringValue); 58 | } 59 | 60 | public Date dateValue(final String... keys) { 61 | final String stringValue = stringValue(keys); 62 | 63 | return stringValue == null ? null : new Date(Long.parseLong(stringValue)); 64 | } 65 | 66 | public Double doubleValue(final String... keys) { 67 | final String stringValue = stringValue(keys); 68 | 69 | return stringValue == null ? null : Double.parseDouble(stringValue); 70 | } 71 | 72 | public Float floatValue(final String... keys) { 73 | final String stringValue = stringValue(keys); 74 | 75 | return stringValue == null ? null : Float.parseFloat(stringValue); 76 | } 77 | 78 | public Integer integerValue(final String... keys) { 79 | final String stringValue = stringValue(keys); 80 | 81 | return stringValue == null ? null : Integer.parseInt(stringValue); 82 | } 83 | 84 | public Long longValue(final String... keys) { 85 | final String stringValue = stringValue(keys); 86 | 87 | return stringValue == null ? null : Long.parseLong(stringValue); 88 | } 89 | 90 | public String stringValue(final String... keys) { 91 | return stringValue(representation(), keys); 92 | } 93 | 94 | public String[] stringArrayValue(final String... keys) { 95 | final JsonArray array = array(keys); 96 | 97 | if (array != null) { 98 | final int size = array.size(); 99 | final String[] stringArray = new String[size]; 100 | for (int idx = 0; idx < size; ++idx) { 101 | stringArray[idx] = array.get(idx).getAsString(); 102 | } 103 | return stringArray; 104 | } 105 | return new String[0]; 106 | } 107 | 108 | //============================================== 109 | // internal implementation 110 | //============================================== 111 | 112 | protected JsonElement elementFrom(final JsonObject jsonObject, final String key) { 113 | JsonElement element = jsonObject.get(key); 114 | 115 | if (element == null) { 116 | element = jsonObject.get("@" + key); 117 | } 118 | 119 | return element; 120 | } 121 | 122 | protected JsonElement navigateTo(final JsonObject startingJsonObject, final String... keys) { 123 | if (keys.length == 0) { 124 | throw new IllegalArgumentException("Must specify one or more keys."); 125 | } 126 | 127 | int keyIndex = 1; 128 | 129 | JsonElement element = elementFrom(startingJsonObject, keys[0]); 130 | 131 | if (!element.isJsonNull() && !element.isJsonPrimitive() && !element.isJsonArray()) { 132 | JsonObject object = element.getAsJsonObject(); 133 | 134 | for ( ; element != null && !element.isJsonPrimitive() && keyIndex < keys.length; ++keyIndex) { 135 | 136 | element = elementFrom(object, keys[keyIndex]); 137 | 138 | if (!element.isJsonPrimitive()) { 139 | 140 | element = elementFrom(object, keys[keyIndex]); 141 | 142 | if (element.isJsonNull()) { 143 | element = null; 144 | } else { 145 | object = element.getAsJsonObject(); 146 | } 147 | } 148 | } 149 | } 150 | 151 | if (element != null) { 152 | if (!element.isJsonNull()) { 153 | if (keyIndex != keys.length) { 154 | throw new IllegalArgumentException("Last name must reference a simple value."); 155 | } 156 | } else { 157 | element = null; 158 | } 159 | } 160 | 161 | return element; 162 | } 163 | 164 | protected JsonObject parse(final String jsonRepresentation) { 165 | try { 166 | return JsonParser.parseString(jsonRepresentation).getAsJsonObject(); 167 | } catch (Exception e) { 168 | e.printStackTrace(); 169 | throw new RuntimeException(e); 170 | } 171 | } 172 | 173 | protected JsonObject representation() { 174 | return representation; 175 | } 176 | 177 | protected String stringValue(final JsonObject startingJsonObject, final String... keys) { 178 | String value = null; 179 | 180 | JsonElement element = navigateTo(startingJsonObject, keys); 181 | 182 | if (element != null) { 183 | value = element.getAsString(); 184 | } 185 | 186 | return value; 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/main/java/co/vaughnvernon/mockroservices/fn/Tuple2.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaughnVernon/mockroservices-java/137ab500d76653cc83ff13db1fcf7e2090d5c6ac/src/main/java/co/vaughnvernon/mockroservices/fn/Tuple2.java -------------------------------------------------------------------------------- /src/main/java/co/vaughnvernon/mockroservices/fn/Tuple3.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaughnVernon/mockroservices-java/137ab500d76653cc83ff13db1fcf7e2090d5c6ac/src/main/java/co/vaughnvernon/mockroservices/fn/Tuple3.java -------------------------------------------------------------------------------- /src/main/java/co/vaughnvernon/mockroservices/journal/EntryBatch.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaughnVernon/mockroservices-java/137ab500d76653cc83ff13db1fcf7e2090d5c6ac/src/main/java/co/vaughnvernon/mockroservices/journal/EntryBatch.java -------------------------------------------------------------------------------- /src/main/java/co/vaughnvernon/mockroservices/journal/EntryStream.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaughnVernon/mockroservices-java/137ab500d76653cc83ff13db1fcf7e2090d5c6ac/src/main/java/co/vaughnvernon/mockroservices/journal/EntryStream.java -------------------------------------------------------------------------------- /src/main/java/co/vaughnvernon/mockroservices/journal/EntryStreamReader.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaughnVernon/mockroservices-java/137ab500d76653cc83ff13db1fcf7e2090d5c6ac/src/main/java/co/vaughnvernon/mockroservices/journal/EntryStreamReader.java -------------------------------------------------------------------------------- /src/main/java/co/vaughnvernon/mockroservices/journal/EntryValue.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaughnVernon/mockroservices-java/137ab500d76653cc83ff13db1fcf7e2090d5c6ac/src/main/java/co/vaughnvernon/mockroservices/journal/EntryValue.java -------------------------------------------------------------------------------- /src/main/java/co/vaughnvernon/mockroservices/journal/Journal.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaughnVernon/mockroservices-java/137ab500d76653cc83ff13db1fcf7e2090d5c6ac/src/main/java/co/vaughnvernon/mockroservices/journal/Journal.java -------------------------------------------------------------------------------- /src/main/java/co/vaughnvernon/mockroservices/journal/JournalPublisher.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaughnVernon/mockroservices-java/137ab500d76653cc83ff13db1fcf7e2090d5c6ac/src/main/java/co/vaughnvernon/mockroservices/journal/JournalPublisher.java -------------------------------------------------------------------------------- /src/main/java/co/vaughnvernon/mockroservices/journal/JournalReader.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaughnVernon/mockroservices-java/137ab500d76653cc83ff13db1fcf7e2090d5c6ac/src/main/java/co/vaughnvernon/mockroservices/journal/JournalReader.java -------------------------------------------------------------------------------- /src/main/java/co/vaughnvernon/mockroservices/journal/Repository.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaughnVernon/mockroservices-java/137ab500d76653cc83ff13db1fcf7e2090d5c6ac/src/main/java/co/vaughnvernon/mockroservices/journal/Repository.java -------------------------------------------------------------------------------- /src/main/java/co/vaughnvernon/mockroservices/journal/StoredSource.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaughnVernon/mockroservices-java/137ab500d76653cc83ff13db1fcf7e2090d5c6ac/src/main/java/co/vaughnvernon/mockroservices/journal/StoredSource.java -------------------------------------------------------------------------------- /src/main/java/co/vaughnvernon/mockroservices/journal/StreamNameBuilder.java: -------------------------------------------------------------------------------- 1 | package co.vaughnvernon.mockroservices.journal; 2 | 3 | public final class StreamNameBuilder { 4 | public static String buildStreamNameFor(final Class streamClass, final String value) { 5 | return streamClass.getSimpleName().toLowerCase() + '_' + value; 6 | } 7 | public static String buildStreamNameFor(final Class streamClass) { 8 | return "cat-" + streamClass.getSimpleName().toLowerCase(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/co/vaughnvernon/mockroservices/kvstore/KVStore.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaughnVernon/mockroservices-java/137ab500d76653cc83ff13db1fcf7e2090d5c6ac/src/main/java/co/vaughnvernon/mockroservices/kvstore/KVStore.java -------------------------------------------------------------------------------- /src/main/java/co/vaughnvernon/mockroservices/messagebus/Message.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaughnVernon/mockroservices-java/137ab500d76653cc83ff13db1fcf7e2090d5c6ac/src/main/java/co/vaughnvernon/mockroservices/messagebus/Message.java -------------------------------------------------------------------------------- /src/main/java/co/vaughnvernon/mockroservices/messagebus/MessageBus.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaughnVernon/mockroservices-java/137ab500d76653cc83ff13db1fcf7e2090d5c6ac/src/main/java/co/vaughnvernon/mockroservices/messagebus/MessageBus.java -------------------------------------------------------------------------------- /src/main/java/co/vaughnvernon/mockroservices/messagebus/MessageExchangeReader.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaughnVernon/mockroservices-java/137ab500d76653cc83ff13db1fcf7e2090d5c6ac/src/main/java/co/vaughnvernon/mockroservices/messagebus/MessageExchangeReader.java -------------------------------------------------------------------------------- /src/main/java/co/vaughnvernon/mockroservices/messagebus/Subscriber.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaughnVernon/mockroservices-java/137ab500d76653cc83ff13db1fcf7e2090d5c6ac/src/main/java/co/vaughnvernon/mockroservices/messagebus/Subscriber.java -------------------------------------------------------------------------------- /src/main/java/co/vaughnvernon/mockroservices/messagebus/Topic.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaughnVernon/mockroservices-java/137ab500d76653cc83ff13db1fcf7e2090d5c6ac/src/main/java/co/vaughnvernon/mockroservices/messagebus/Topic.java -------------------------------------------------------------------------------- /src/main/java/co/vaughnvernon/mockroservices/model/Command.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaughnVernon/mockroservices-java/137ab500d76653cc83ff13db1fcf7e2090d5c6ac/src/main/java/co/vaughnvernon/mockroservices/model/Command.java -------------------------------------------------------------------------------- /src/main/java/co/vaughnvernon/mockroservices/model/DomainEvent.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaughnVernon/mockroservices-java/137ab500d76653cc83ff13db1fcf7e2090d5c6ac/src/main/java/co/vaughnvernon/mockroservices/model/DomainEvent.java -------------------------------------------------------------------------------- /src/main/java/co/vaughnvernon/mockroservices/model/Id.java: -------------------------------------------------------------------------------- 1 | package co.vaughnvernon.mockroservices.model; 2 | 3 | import java.util.UUID; 4 | 5 | public final class Id { 6 | public final String value; 7 | 8 | public static Id fromExisting(final String referencedId) { 9 | return new Id(referencedId); 10 | } 11 | 12 | public static Id unique() { 13 | return new Id(); 14 | } 15 | 16 | @Override 17 | public int hashCode() { 18 | return value.hashCode(); 19 | } 20 | 21 | @Override 22 | public boolean equals(final Object other) { 23 | if (other == this) { 24 | return true; 25 | } 26 | if (other == null || other.getClass() != Id.class) { 27 | return false; 28 | } 29 | 30 | final Id otherId = (Id) other; 31 | 32 | return this.value.equals(otherId.value); 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | return "Id[value=" + value + "]"; 38 | } 39 | 40 | private Id() { 41 | this.value = UUID.randomUUID().toString(); 42 | } 43 | 44 | private Id(final String referencedId) { 45 | this.value = referencedId; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/co/vaughnvernon/mockroservices/model/ProcessManager.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaughnVernon/mockroservices-java/137ab500d76653cc83ff13db1fcf7e2090d5c6ac/src/main/java/co/vaughnvernon/mockroservices/model/ProcessManager.java -------------------------------------------------------------------------------- /src/main/java/co/vaughnvernon/mockroservices/model/SourceType.java: -------------------------------------------------------------------------------- 1 | package co.vaughnvernon.mockroservices.model; 2 | 3 | public interface SourceType { } 4 | -------------------------------------------------------------------------------- /src/main/java/co/vaughnvernon/mockroservices/model/SourcedEntity.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaughnVernon/mockroservices-java/137ab500d76653cc83ff13db1fcf7e2090d5c6ac/src/main/java/co/vaughnvernon/mockroservices/model/SourcedEntity.java -------------------------------------------------------------------------------- /src/main/java/co/vaughnvernon/mockroservices/serialization/Serialization.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaughnVernon/mockroservices-java/137ab500d76653cc83ff13db1fcf7e2090d5c6ac/src/main/java/co/vaughnvernon/mockroservices/serialization/Serialization.java -------------------------------------------------------------------------------- /src/test/java/co/vaughnvernon/SomeEventTypeName.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaughnVernon/mockroservices-java/137ab500d76653cc83ff13db1fcf7e2090d5c6ac/src/test/java/co/vaughnvernon/SomeEventTypeName.java -------------------------------------------------------------------------------- /src/test/java/co/vaughnvernon/SomeOtherEventTypeName.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaughnVernon/mockroservices-java/137ab500d76653cc83ff13db1fcf7e2090d5c6ac/src/test/java/co/vaughnvernon/SomeOtherEventTypeName.java -------------------------------------------------------------------------------- /src/test/java/co/vaughnvernon/mockroservices/Person.java: -------------------------------------------------------------------------------- 1 | package co.vaughnvernon.mockroservices; 2 | 3 | import java.util.Date; 4 | import java.util.List; 5 | import java.util.UUID; 6 | 7 | import co.vaughnvernon.mockroservices.model.DomainEvent; 8 | import co.vaughnvernon.mockroservices.model.SourcedEntity; 9 | 10 | public class Person { 11 | public final Date birthDate; 12 | public final String name; 13 | 14 | @Override 15 | public boolean equals(final Object other) { 16 | if (other == null || other.getClass() != Person.class) { 17 | return false; 18 | } 19 | 20 | final Person otherPerson = (Person) other; 21 | 22 | return this.name.equals(otherPerson.name) && this.birthDate.equals(otherPerson.birthDate); 23 | } 24 | 25 | public Person(final String name, final Date birthDate) { 26 | this.name = name; 27 | this.birthDate = birthDate; 28 | } 29 | 30 | public static class ClientPerson { 31 | public String name; 32 | public Date birthDate; 33 | } 34 | 35 | public static class ClientPersonWithPrivateSetter { 36 | public String name; 37 | public Date birthDate; 38 | 39 | public ClientPersonWithPrivateSetter() { } 40 | 41 | public static ClientPersonWithPrivateSetter instance(String name, Date birthDate) { 42 | return new ClientPersonWithPrivateSetter(name, birthDate); 43 | } 44 | private ClientPersonWithPrivateSetter(String name, Date birthDate) { 45 | this.name = name; 46 | this.birthDate = birthDate; 47 | } 48 | } 49 | 50 | public static class PersonES extends SourcedEntity { 51 | // [JsonConstructor] 52 | public PersonES(String name, int age, Date birthDate) { 53 | apply(new PersonNamed(UUID.randomUUID().toString(), name, age, birthDate)); 54 | } 55 | 56 | public PersonES(List stream, int streamVersion) { 57 | super(stream, streamVersion); 58 | } 59 | 60 | public String id; 61 | public String name; 62 | public int age; 63 | public Date birthDate; 64 | 65 | public void when(PersonNamed personNamed) 66 | { 67 | id = personNamed.id; 68 | name = personNamed.name; 69 | age = personNamed.age; 70 | } 71 | 72 | @Override 73 | public boolean equals(final Object other) { 74 | if (other == null || other.getClass() != PersonES.class) { 75 | return false; 76 | } 77 | 78 | final PersonES otherPerson = (PersonES) other; 79 | 80 | return this.id.equals(otherPerson.id) && this.name.equals(otherPerson.name) && this.age == otherPerson.age; 81 | } 82 | // public override int GetHashCode() => 31 * Id.GetHashCode() * Name.GetHashCode() * Age.GetHashCode(); 83 | // public override string ToString() => $"PersonES [Id={Id} Name={Name} Age={Age}]"; 84 | } 85 | public static class PersonNamed extends DomainEvent { 86 | public String id; 87 | public String name; 88 | public int age; 89 | public Date birthDate; 90 | 91 | public PersonNamed(String id, String name, int age, Date birthDate) 92 | { 93 | this.id = id; 94 | this.name = name; 95 | this.age = age; 96 | this.birthDate = birthDate; 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/test/java/co/vaughnvernon/mockroservices/Product.java: -------------------------------------------------------------------------------- 1 | package co.vaughnvernon.mockroservices; 2 | 3 | import java.util.List; 4 | 5 | import co.vaughnvernon.mockroservices.model.DomainEvent; 6 | import co.vaughnvernon.mockroservices.model.SourcedEntity; 7 | 8 | public class Product extends SourcedEntity { 9 | public String id; 10 | public String name; 11 | public String description; 12 | public long price; 13 | 14 | public Product(final String id, final String name, final String description, final long price) { 15 | apply(new ProductDefined(id, name, description, price)); 16 | } 17 | 18 | public Product(final String id, final String name, final String description, final long price, final long validOn) { 19 | apply(new ProductDefined(id, name, description, price, validOn)); 20 | } 21 | 22 | public Product(final List eventStream, final int streamVersion) { 23 | super(eventStream, streamVersion); 24 | } 25 | 26 | public void changeDescription(final String description) { 27 | apply(new ProductDescriptionChanged(description)); 28 | } 29 | 30 | public void changeDescription(final String description, final long validOn) { 31 | apply(new ProductDescriptionChanged(description, validOn)); 32 | } 33 | 34 | public void changeName(final String name) { 35 | apply(new ProductNameChanged(name)); 36 | } 37 | 38 | public void changeName(final String name, final long validOn) { 39 | apply(new ProductNameChanged(name, validOn)); 40 | } 41 | 42 | public void changePrice(final long price) { 43 | apply(new ProductPriceChanged(price)); 44 | } 45 | 46 | public void changePrice(final long price, final long validOn) { 47 | apply(new ProductPriceChanged(price, validOn)); 48 | } 49 | 50 | @Override 51 | public boolean equals(Object other) { 52 | if (other == null || other.getClass() != Product.class) { 53 | return false; 54 | } 55 | 56 | final Product otherProduct = (Product) other; 57 | 58 | return this.name.equals(otherProduct.name) && 59 | this.description.equals(otherProduct.description) && 60 | this.price == otherProduct.price; 61 | } 62 | 63 | public void when(final ProductDefined event) { 64 | this.id = event.id; 65 | this.name = event.name; 66 | this.description = event.description; 67 | this.price = event.price; 68 | } 69 | 70 | public void when(final ProductDescriptionChanged event) { 71 | this.description = event.description; 72 | } 73 | 74 | public void when(final ProductNameChanged event) { 75 | this.name = event.name; 76 | } 77 | 78 | public void when(final ProductPriceChanged event) { 79 | this.price = event.price; 80 | } 81 | 82 | public static class ProductDefined extends DomainEvent { 83 | public final String id; 84 | public final String description; 85 | public final String name; 86 | public final long price; 87 | 88 | public ProductDefined(final String id, final String name, final String description, final long price) { 89 | this(id, name, description, price, System.currentTimeMillis()); 90 | } 91 | ProductDefined(final String id, final String name, final String description, final long price, final long validOn) { 92 | super(validOn, 0); 93 | this.id = id; 94 | this.name = name; 95 | this.description = description; 96 | this.price = price; 97 | } 98 | 99 | @Override 100 | public boolean equals(Object other) { 101 | if (other == null || other.getClass() != ProductDefined.class) { 102 | return false; 103 | } 104 | 105 | final ProductDefined otherProductDefined = (ProductDefined) other; 106 | 107 | return this.id.equals(otherProductDefined.id) && 108 | this.name.equals(otherProductDefined.name) && 109 | this.description.equals(otherProductDefined.description) && 110 | this.price == otherProductDefined.price && 111 | eventVersion == otherProductDefined.eventVersion; 112 | } 113 | } 114 | 115 | public static class ProductDescriptionChanged extends DomainEvent { 116 | public final String description; 117 | 118 | public ProductDescriptionChanged(final String description) { 119 | this(description, System.currentTimeMillis()); 120 | } 121 | 122 | ProductDescriptionChanged(final String description, final long validOn) { 123 | super(validOn, 0); 124 | this.description = description; 125 | } 126 | 127 | @Override 128 | public boolean equals(Object other) { 129 | if (other == null || other.getClass() != ProductDescriptionChanged.class) { 130 | return false; 131 | } 132 | 133 | final ProductDescriptionChanged otherProductDescriptionChanged = (ProductDescriptionChanged) other; 134 | 135 | return this.description.equals(otherProductDescriptionChanged.description) && 136 | eventVersion == otherProductDescriptionChanged.eventVersion; 137 | } 138 | } 139 | 140 | public static class ProductNameChanged extends DomainEvent { 141 | public final String name; 142 | 143 | public ProductNameChanged(final String name) { 144 | this(name, System.currentTimeMillis()); 145 | } 146 | 147 | ProductNameChanged(final String name, final long validOn) { 148 | super(validOn, 0); 149 | this.name = name; 150 | } 151 | 152 | @Override 153 | public boolean equals(Object other) { 154 | if (other == null || other.getClass() != ProductNameChanged.class) { 155 | return false; 156 | } 157 | 158 | final ProductNameChanged otherProductNameChanged = (ProductNameChanged) other; 159 | 160 | return this.name.equals(otherProductNameChanged.name) && 161 | eventVersion == otherProductNameChanged.eventVersion; 162 | } 163 | } 164 | 165 | public static class ProductPriceChanged extends DomainEvent { 166 | public final long price; 167 | 168 | public ProductPriceChanged(final long price) { 169 | this(price, System.currentTimeMillis()); 170 | } 171 | 172 | ProductPriceChanged(final long price, final long validOn) { 173 | super(validOn, 0); 174 | this.price = price; 175 | } 176 | 177 | @Override 178 | public boolean equals(Object other) { 179 | if (other == null || other.getClass() != ProductPriceChanged.class) { 180 | return false; 181 | } 182 | 183 | final ProductPriceChanged otherProductPriceChanged = (ProductPriceChanged) other; 184 | 185 | return this.price == otherProductPriceChanged.price && 186 | eventVersion == otherProductPriceChanged.eventVersion; 187 | } 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/test/java/co/vaughnvernon/mockroservices/journal/IntegrationTest.java: -------------------------------------------------------------------------------- 1 | package co.vaughnvernon.mockroservices.journal; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import java.util.Date; 6 | import java.util.UUID; 7 | 8 | import org.junit.Test; 9 | 10 | import co.vaughnvernon.mockroservices.Product; 11 | import co.vaughnvernon.mockroservices.journal.JournalPublisherTest.TestSubscriber; 12 | import co.vaughnvernon.mockroservices.messagebus.MessageBus; 13 | import co.vaughnvernon.mockroservices.messagebus.Topic; 14 | 15 | public class IntegrationTest { 16 | 17 | @Test 18 | public void writeReadBiTemporalEvents() { 19 | ProductRepository repository = new ProductRepository("bi-product-journal"); 20 | long y2018 = new Date(1000000l).getTime(); //var y2018 = new DateTimeOffset(2018, 1, 1, 0, 0, 0, TimeSpan.Zero); 21 | long y2019 = new Date(2000000l).getTime(); //var y2019 = new DateTimeOffset(2019, 1, 1, 0, 0, 0, TimeSpan.Zero); 22 | long y2020 = new Date(3000000l).getTime(); //var y2020 = new DateTimeOffset(2020, 1, 1, 0, 0, 0, TimeSpan.Zero); 23 | 24 | String id = UUID.randomUUID().toString(); 25 | Product product = new Product(id, "dice-fuz-1", "Fuzzy dice.", 999, y2018); 26 | product.changeName("dice-fuzzy-1", y2020); 27 | product.changeDescription("Fuzzy dice, and all.", y2020); 28 | product.changePrice(995, y2019); 29 | product.changeName("dice-fizzy-1", y2019); 30 | repository.save(Product.class, product); 31 | 32 | Product productAt2018 = repository.productOfId(Product.class, id, y2018); 33 | assertEquals(5, productAt2018.currentVersion); 34 | assertEquals("dice-fuz-1", productAt2018.name); 35 | assertEquals("Fuzzy dice.", productAt2018.description); 36 | assertEquals(999, productAt2018.price); 37 | 38 | Product productAt2019 = repository.productOfId(Product.class, id, y2019); 39 | assertEquals(5, productAt2019.currentVersion); 40 | assertEquals("dice-fizzy-1", productAt2019.name); 41 | assertEquals("Fuzzy dice.", productAt2019.description); 42 | assertEquals(995, productAt2019.price); 43 | 44 | Product productAt2020 = repository.productOfId(Product.class, id, y2020); 45 | assertEquals(5, productAt2020.currentVersion); 46 | assertEquals("dice-fuzzy-1", productAt2020.name); 47 | assertEquals("Fuzzy dice, and all.", productAt2020.description); 48 | assertEquals(995, productAt2020.price); 49 | } 50 | 51 | @Test 52 | public void fromRepositoryToProjection() throws Exception { 53 | MessageBus messageBus = MessageBus.start("test-bus-product"); 54 | Topic topic = messageBus.openTopic("cat-product"); 55 | String journalName = "product-journal"; 56 | JournalPublisher journalPublisher = JournalPublisher.using(journalName, messageBus.name, topic.name); 57 | TestSubscriber subscriber = new JournalPublisherTest.TestSubscriber(); 58 | topic.subscribe(subscriber); 59 | 60 | ProductRepository repository = new ProductRepository(journalName); 61 | 62 | Product product1 = new Product(UUID.randomUUID().toString(), "dice-fuz-1", "Fuzzy dice.", 999); 63 | product1.changeName("dice-fuzzy-1"); 64 | product1.changeDescription("Fuzzy dice, and all."); 65 | product1.changePrice(995); 66 | repository.save(Product.class, product1); 67 | 68 | Product product2 = new Product(UUID.randomUUID().toString(), "dice-fuz-2", "Fuzzy dice.", 999); 69 | product2.changeName("dice-fuzzy-2"); 70 | product2.changeDescription("Fuzzy dice, and all 2."); 71 | product2.changePrice(1000); 72 | repository.save(Product.class, product2); 73 | 74 | subscriber.waitForExpectedMessages(8); 75 | topic.close(); 76 | journalPublisher.close(); 77 | 78 | assertEquals(8, subscriber.handledMessages.size()); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/test/java/co/vaughnvernon/mockroservices/journal/JournalPublisherTest.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaughnVernon/mockroservices-java/137ab500d76653cc83ff13db1fcf7e2090d5c6ac/src/test/java/co/vaughnvernon/mockroservices/journal/JournalPublisherTest.java -------------------------------------------------------------------------------- /src/test/java/co/vaughnvernon/mockroservices/journal/JournalTest.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaughnVernon/mockroservices-java/137ab500d76653cc83ff13db1fcf7e2090d5c6ac/src/test/java/co/vaughnvernon/mockroservices/journal/JournalTest.java -------------------------------------------------------------------------------- /src/test/java/co/vaughnvernon/mockroservices/journal/ProductRepository.java: -------------------------------------------------------------------------------- 1 | package co.vaughnvernon.mockroservices.journal; 2 | 3 | import co.vaughnvernon.mockroservices.Product; 4 | 5 | public class ProductRepository extends Repository { 6 | // readonly 7 | private Journal journal; 8 | // readonly 9 | private EntryStreamReader reader; 10 | 11 | public Product productOfId(String id) { 12 | EntryStream stream = reader.streamFor(id); 13 | return new Product(toSourceStream(stream.stream), stream.streamVersion); 14 | } 15 | 16 | // public Product productOfId(final Class streamClass, String id) { 17 | // EntryStream stream = reader.streamFor(streamClass, id); 18 | // return new Product(toSourceStream(stream.stream), stream.streamVersion); 19 | // } 20 | 21 | public Product productOfId(final Class streamClass, String id, long validOn) { 22 | EntryStream stream = reader.streamFor(streamClass, id); 23 | return new Product(toSourceStream(stream.stream, validOn), stream.streamVersion); 24 | } 25 | 26 | public void save(Product product) { 27 | journal.write(product.id, product.nextVersion(), toBatch(product.applied)); 28 | } 29 | 30 | public void save(final Class streamClass, T product) { 31 | journal.write(streamClass, product.id, product.nextVersion(), toBatch(product.applied)); 32 | } 33 | 34 | //internal 35 | public ProductRepository(String journalName) 36 | { 37 | journal = Journal.open(journalName); 38 | reader = journal.streamReader(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/test/java/co/vaughnvernon/mockroservices/journal/RepositoryTest.java: -------------------------------------------------------------------------------- 1 | package co.vaughnvernon.mockroservices.journal; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import java.time.Instant; 6 | import java.util.Date; 7 | 8 | import org.junit.Test; 9 | 10 | import co.vaughnvernon.mockroservices.Person; 11 | import co.vaughnvernon.mockroservices.Person.PersonES; 12 | 13 | public class RepositoryTest { 14 | @Test 15 | public void testSaveFind() 16 | { 17 | PersonRepository repository = new PersonRepository(); 18 | Person.PersonES person = new Person.PersonES("Joe", 40, Date.from(Instant.now())); 19 | repository.save(person); 20 | Person.PersonES joe = repository.personOfId(person.id); 21 | assertEquals(person, joe); 22 | } 23 | 24 | @Test 25 | public void testSaveWithStreamPrefixAndFind() 26 | { 27 | PersonRepository repository = new PersonRepository(); 28 | Person.PersonES person = new Person.PersonES("Joe", 40, Date.from(Instant.now())); 29 | repository.save(PersonES.class, person); 30 | Person.PersonES joe = repository.personOfId(StreamNameBuilder.buildStreamNameFor(PersonES.class, person.id)); 31 | assertEquals(person, joe); 32 | } 33 | 34 | @Test 35 | public void testSaveFindWithStreamPrefix() 36 | { 37 | PersonRepository repository = new PersonRepository(); 38 | Person.PersonES person = new Person.PersonES("Joe", 40, Date.from(Instant.now())); 39 | repository.save(PersonES.class, person); 40 | Person.PersonES joe = repository.personOfId(PersonES.class, person.id); 41 | assertEquals(person, joe); 42 | } 43 | 44 | public class PersonRepository extends Repository { 45 | // readonly 46 | private Journal journal; 47 | // readonly 48 | private EntryStreamReader reader; 49 | 50 | public PersonES personOfId(String id) 51 | { 52 | EntryStream stream = reader.streamFor(id); 53 | return new PersonES(toSourceStream(stream.stream), stream.streamVersion); 54 | } 55 | 56 | public PersonES personOfId(final Class streamClass, String id) 57 | { 58 | EntryStream stream = reader.streamFor(streamClass, id); 59 | return new PersonES(toSourceStream(stream.stream), stream.streamVersion); 60 | } 61 | 62 | public void save(PersonES person) { 63 | journal.write(person.id, person.nextVersion(), toBatch(person.applied)); 64 | } 65 | 66 | public void save(final Class streamClass, T person) { 67 | journal.write(streamClass, person.id, person.nextVersion(), toBatch(person.applied)); 68 | } 69 | //internal 70 | private PersonRepository() 71 | { 72 | journal = Journal.open("repo-test"); 73 | reader = journal.streamReader(); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/test/java/co/vaughnvernon/mockroservices/journal/SomeEventTypeName.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaughnVernon/mockroservices-java/137ab500d76653cc83ff13db1fcf7e2090d5c6ac/src/test/java/co/vaughnvernon/mockroservices/journal/SomeEventTypeName.java -------------------------------------------------------------------------------- /src/test/java/co/vaughnvernon/mockroservices/journal/StreamNameBuilderTest.java: -------------------------------------------------------------------------------- 1 | package co.vaughnvernon.mockroservices.journal; 2 | 3 | import co.vaughnvernon.mockroservices.Person; 4 | import org.junit.Test; 5 | 6 | import static org.junit.Assert.assertEquals; 7 | 8 | public class StreamNameBuilderTest { 9 | 10 | @Test 11 | public void testThatStreamNameBuilderBuildsCorrectStream() { 12 | assertEquals("person_1234", StreamNameBuilder.buildStreamNameFor(Person.class, "1234")); 13 | } 14 | 15 | @Test 16 | public void TestThatStreamNameBuilderBuildsCorrectCategoryStream() { 17 | assertEquals("cat-person", StreamNameBuilder.buildStreamNameFor(Person.class)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/test/java/co/vaughnvernon/mockroservices/kvstore/KVStoreTest.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaughnVernon/mockroservices-java/137ab500d76653cc83ff13db1fcf7e2090d5c6ac/src/test/java/co/vaughnvernon/mockroservices/kvstore/KVStoreTest.java -------------------------------------------------------------------------------- /src/test/java/co/vaughnvernon/mockroservices/messagebus/MessageBusTest.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaughnVernon/mockroservices-java/137ab500d76653cc83ff13db1fcf7e2090d5c6ac/src/test/java/co/vaughnvernon/mockroservices/messagebus/MessageBusTest.java -------------------------------------------------------------------------------- /src/test/java/co/vaughnvernon/mockroservices/messagebus/MessageExchangeReaderTest.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaughnVernon/mockroservices-java/137ab500d76653cc83ff13db1fcf7e2090d5c6ac/src/test/java/co/vaughnvernon/mockroservices/messagebus/MessageExchangeReaderTest.java -------------------------------------------------------------------------------- /src/test/java/co/vaughnvernon/mockroservices/model/EventSourcedRootEntityTest.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaughnVernon/mockroservices-java/137ab500d76653cc83ff13db1fcf7e2090d5c6ac/src/test/java/co/vaughnvernon/mockroservices/model/EventSourcedRootEntityTest.java -------------------------------------------------------------------------------- /src/test/java/co/vaughnvernon/mockroservices/model/TestableDomainEvent.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaughnVernon/mockroservices-java/137ab500d76653cc83ff13db1fcf7e2090d5c6ac/src/test/java/co/vaughnvernon/mockroservices/model/TestableDomainEvent.java -------------------------------------------------------------------------------- /src/test/java/co/vaughnvernon/mockroservices/serialization/SerializationTest.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaughnVernon/mockroservices-java/137ab500d76653cc83ff13db1fcf7e2090d5c6ac/src/test/java/co/vaughnvernon/mockroservices/serialization/SerializationTest.java --------------------------------------------------------------------------------