I'm evaluating a co-worker's pitch using Alloy to model the problem and gain insight into additional questions/constraints that are required to meet the specification.
I believe that Alloy is the right tool for this scope. I also believe I need a dynamic model to illustrate the sequence of actions and identify cases where additional constraints are needed.
Problem: A Member can spend points to purchase Vouchers for an Offer, and redeem or return them. The member cannot net out more points than spent by returning. The member cannot receive more benefits than vouchers.
Model:
open util/ordering[Time]
sig Time {}
// A membership may have a point balance
sig Membership {
vouchers: Voucher
}
sig Voucher {
, voucher_for: one VoucheredOffer
, voucher_from: one PurchasableOffer
}
// Purchasable offers may have some point cost
sig PurchasableOffer {
, grants_for: one VoucheredOffer
}
sig VoucheredOffer {}
// Events
abstract sig Event {
, time: Time
, memberships: Membership
}
one sig init extends Event {} {
first = time
}
// PurchaseVoucher
// A membership receives a voucher
// Additionally, membership may spend points
sig purchaseVoucher extends Event {
, membership: Membership
, offer: PurchasableOffer
, voucher: Voucher
} {
voucher = voucher_from.offer
memberships.vouchers = time.prev.#memberships.vouchers + voucher
}
// ReturnVoucher
// A membership returns a voucher, and if points were spent, the points are returned
// ReserveVoucher
// A membership reserves a voucher
// RedeemVoucher
// Membership redeems a voucher, receiving a benefit.
// Assertions/Invariants/Checks
// A membership's points returned does not exceed points spent.
// A membership does not receive more benefits than total vouchers purchased minus vouchers returned.
I can't figure out how to model purchaseVoucher such that it adds a voucher to the membership's vouchers relation. Or I'm just doing it wrong. I am trying to use signatures to model the events because it presents a better visualization and simpler construction than using predicates. Further, if I am going to use predicates, I would just model this in TLA+.
My book gives examples of how to do this, but the general strategy with events is to make the state signatures time dependent. For example,
vouchers: Voucher
becomes
vouchers: Voucher -> Time
As Peter noted, you can also use Electrum which makes this easier by giving you a keyword for time-varying components.
Related
I need to implement face recognition using azure face api . I have developed a programme which is able to find similiar faces using .net SDK . For my use case ,I need to click photo of a person from the webcam and find matching faces from images kept in azure cloud storage . Now, there could be thousand of images in azure cloud storage and in my current implementation of face recognition ,I'm iterating through all the images(kept in azure cloud storage ) and then matching them with the webcam image .
The concern here is :
The face api (provided by azure ) charges 1 dollar per thousand call . Is there a way the search could be optimized such that i don't have to scan the faces which i have already scanned for previous searches
public async Task<List<DetectedFaceAttributes>> FindSimiliarFacesWithAttributesFromContainer(IFaceClient client, string RECOGNITION_MODEL1, string sourceImageFileName)
{
string url = BlobBaseURL;
string sourceurl = sourceContainerURL;
var imagesInNovotraxContainer = await _blobService.GetNames();
IList<Guid?> targetFaceIds = new List<Guid?>();
var faceList = new List<DetectedFaceAttributes>();
// Detect faces from source image url.
IList<DetectedFace> detectedFaces = await DetectFaceRecognize(client, $"{sourceurl}{sourceImageFileName}", RECOGNITION_MODEL1);
if (detectedFaces.Any())
{
foreach (var targetImageFileName in imagesInNovotraxContainer)
{
var faceattribute = new DetectedFaceAttributes();
// Detect faces from target image url.
var faces = await DetectFaceRecognizeWithAttributes(client, $"{url}{targetImageFileName}");
// Add detected faceId to list of GUIDs.
if (faces.Any())
{
targetFaceIds.Add(faces[0].FaceId.Value);
faceattribute.DetectedFace = faces[0];
faceattribute.ImageFileName = targetImageFileName;
faceList.Add(faceattribute);
}
}
// Find a similar face(s) in the list of IDs. Comapring only the first in list for testing purposes.
IList<SimilarFace> similarResults = await client.Face.FindSimilarAsync(detectedFaces[0].FaceId.Value, null, null, targetFaceIds);
var similiarFaceIDs = similarResults.Select(y => y.FaceId).ToList();
var returnDataTypefaceList = faceList.Where(x => similiarFaceIDs.Contains(x.DetectedFace.FaceId.Value)).ToList();
return returnDataTypefaceList;
}
else
{
throw new Exception("no face detected in captured photo ");
}
public async Task<List<DetectedFace>> DetectFaceRecognize(IFaceClient faceClient, string url, string RECOGNITION_MODEL1)
{
// Detect faces from image URL. Since only recognizing, use the recognition model 1.
IList<DetectedFace> detectedFaces = await faceClient.Face.DetectWithUrlAsync(url, recognitionModel: RECOGNITION_MODEL1);
//if (detectedFaces.Any())
//{
// Console.WriteLine($"{detectedFaces.Count} face(s) detected from image `{Path.GetFileName(url)}` with ID : {detectedFaces.First().FaceId}");
//}
return detectedFaces.ToList();
}
Your implementation is not totally clear for me in terms of calls to Face API / your storage (what's behind "DetectFaceRecognizeWithAttributes"). But I think you are right in the fact that you missed something and your global processing is over costly.
What you should do depends on your target:
Is it face "identification"?
Or face "similarity"?
Both have the same logic, but they are using different API operations
Case 1 - Face identification
Process
The global process is the following: you will use a "Person Group" or "Large Person Group" (depending of the number of persons you have) to store data about faces that you already know (the one in you storage), and you will use this group to "identify" a new face. with that, you will do "1-n" search, not "1-1" as you do right now.
Initial setup (group creation):
Choose if you need Person Group or Large Person group, here are the actual limits depending on your pricing:
Person Group:
Free-tier subscription quota: 1,000 person groups. Each holds up to 1,000 persons.
S0-tier subscription quota: 1,000,000 person groups. Each holds up to 10,000 persons.
Large Person Group:
It can hold up to 1,000,000 persons.
Free-tier subscription quota: 1,000 large person groups.
S0-tier subscription quota: 1,000,000 large person groups.
Here I am using Person Group in the explanation, but it's the same methods.
When you know the one you need, create it using "Create" operation.
Then, for each person, you will have to create a "PersonGroup Person" using "PersonGroup Person - Create", and add the corresponding faces to it using "PersonGroup Person - Add Face". Once it is done, you never need to reprocess "detect" operation on those faces.
Then for the "run" part
When you have a new image that you want to compare:
Detect faces in your image with Detect endpoint of Face API
Get the face Ids of your result
Call Identify endpoint of Face API to try to identify those face Ids with your (large) person group
To limit the number of call, you can even do batches of identification calls (up to 10 "input" face Ids in 1 call - see doc).
Case 2 - Face similarity
Here you can use a "Face List" or "Large Face List" to store the faces that you already know, and pass the id of this list when calling "Find Similar" operation. Example with FaceList:
Start with "FaceList - Create" to create your list (doc)
Use "FaceList - Add Face" to add all the faces that you have currently in your blob (doc)
Then for the run, when you call "Find Similar", provide the ID of your FaceList in "faceListId" parameter and the id of the face you want to compare (from Face Detect call)
I am creating a capsule that will remind me and people in my contacts to vote on Election Day 2020. I am working on the models. I believe I need to model the concepts "Reminders" and "WhenPollsAreOpen". I am stuck on Reminders.
It seems to me that I want to create something like .reminder library capsule to handle reminding in general and then a specific model of reminding people to vote in elections.
structure (reminderPreferences) {
description (preferences about a reminder)
property (name) {
type (Name)
min (Required)
}
property (description) {
type (Description)
min (Required)
}
property (isactive) {
type (boolean)
min (Required)
}
I want the model to compile cleanly, but I get multiple "can't find type for property" error messagesenter image description here
I use CouchDB and I would like all my users to have unique emails. I would expect that the database return a status 400 (bad request) when I try to duplicate an email.
But since there is no way to define constraints in CouchDB, I should implement it myself, my question is:
In which layer of my application should this rule stand?
(1) Domain objects layer
I don't really know how to implement it in this layer
(2) Interactor layer
This constraint could be implemented in interactors here since there is the place where business rules stand. But if there is multiple rules for single document it could add unnecessary complexity...
function createUser(userData) {
let email = userData.email;
let exist = await userDB.userExist(email);
if(exist) {
// return status 400
} else {
// create user
}
}
(3) Database gateway layer
The constraint can also be implemented in the database gateway layer. Usually we'll have a gateway for each specific entity. But does that means that external services adapters contains a bit of business logic?
class userDB() {
constructor(opts) {
this.db = opts.db.connect();
}
async userExist(email) {
return await this.db.fetchByView('email', email);
}
async create(email) {
let exist = await this.userExist(data.email);
if(exist) {
// throw error
} else {
// create the user
}
}
}
Unique email address is a very old DDD topic. It relates to the set validation.
The simplest (and from my point of view is also the best) is to place a constraint at the database level.
From what I know, the only whay to create an unique constraint in CouchDB is to use the _id field so you can use this solution. This idea is to put the email in the _id field.
let exist = await this.userExist(data.email);
if(exist) { // throw
error } else { // create the user }
This method is not safe for concurrent updates. Two users can be created at the same time. Imagine that for both the requests the this.userExist(data.email) is returning false.
I am not a CouchDB expert but from pure Clean Architecture perspective ensuring unique email addresses is a business rule which should be implemented in an interactor.
With this approach ur business rule would remain intact even if u would once decide to replace the detail CouchDB with some other detail - another storage system.
I am currently working on a DDD system that is composed out of several bounded contexts. 2 of them are:
Context "account management": Only staff members are allowed to work here. The idea is to manage customer accounts (like address, phone numbers, contacts etc etc) and to verify the account of a customer (basically checking if the data the customer supplied is valid).
Context "website": I can login as a customer and edit my data (change my address for example)
Here is the issue:
A user logged in into the account management context is per definition an employee. So I can assume that changes made here are "trustworthy" in the sense of "the data is verified". A simplified variant of the appservice looks like this:
class AccountAppService
{
public function changeAddress(string $accountId, string $address) : void
{
$account = $this->accountRepository->ofId(new Guid($accountId));
$account->changeAddress(new Address($address));
}
{
This is the appservice I am calling when an employee is changing an address. Note that there is no IdentityService that I inject/use in order to know who the employee is as this is not interesting here. The Account entity would emit an AccountAddressChanged event after successfully calling its changeAddress() method like so
class Account implements Entity
{
public function changeAddress(Address $address) : void
{
$this->address = $address;
DomainEventSubscriber::instance()->publish(new AccountAddressChanged($this));
}
}
But I also need to reflect changes as soon as a customer edits data on the website. I plan to do this async via events a la "AccountAddressChangedViaWebsite". The account management context will subscribe and handle that event, setting the corresponding account to "unverified" again. So a simplified subscriber of the account management context could look like:
class AccountAddressChangedViaWebsiteSubscriber
{
public function handle(AccountAddressChangedViaWebsite $event) : void
{
$accountId = $event->accountId();
$address = $event->getAddress();
$this->accountService->changeAddress($accountId, $address);
}
}
Now the question: Employees call the appservice directly, customers via subscribers. If we say "we have to reverify an account after the customer updates his data" it sounds like a domain concept.
Domain concepts fit into entities or domain services, but not into application services or subscribers for what I know. It implies to me that the following should be avoided (note the last line calling unverifyAccount()):
class AccountAddressChangedViaWebsiteSubscriber
{
public function handle(AccountAddressChangedViaWebsite $event) : void
{
$accountId = $event->accountId();
$address = $event->getAddress();
$this->accountService->changeAddress($accountId, $address);
$this->accountService->unverifyAccount($accountId);
}
}
This is domain logic that is somewhat hidden in a subscriber which seems odd. I have the gut feeling that this should be the responsibility of a domain service, but how would the domain service know that it is called by an external event (via subscriber) or a command?
I could pass a sort of "Originator" ValueObject that tells me wheter the user causing this is an employee or an external system. Example:
class OriginatorService
{
public function changeAddress(Originator $originator, Account $account, Address $address) : void
{
$account->changeAddress($address);
if(($originator instanceof Employee) === false) {
$account->unverify();
}
}
}
Here I delegate the responsibility of what to do to a domain service. But might double dispatching the OriginatorService into the Account entity be a good solution? This way the entity could check who caused the change via asking the passed in originatorService and could unverify itself.
I guess I am going down the DDD rabbit hole here, but what are your experiences/best practises in such a case?
The simplest answer is probably introduce UnverifiedAddress as a concept in your model, rather than trying to treat "Address" as a universal idea with the verification bolted on as an afterthought.
There is a big design flaw here, but I'm having trouble solving it:
The business need is a little involved so I'll try to keep this simple.
We have a table with purchases, and a table for returns. When a return is made, we have to find match that return to the oldest purchase in the db, and record that in a "returns applied" table.
So, when I insert a Return, within that transaction, I need to apply the return to a purchase record.
As it stands now, we have a service that calls the repository for the insert. The service needs to know what the key is of that inserted record, so that it can finish the transaction by inserting an "applied" record using that key.
We're basically stuck because my understanding is that a repository should not return this kind of data. Doesn't this defeat the idea of the Repository being a collection?
What is the alternative?
CLARIFICATION:
We have a Purchase table, a Return table, and an Applied table
The applied table looks like this
purchaseId returnId qtyReturned
So when a return is inserted I need the id of a purchase (decided by some business rules) and the id of the newly inserted return.
I suppose the following according to your question:
public class Purchase {
// ReturnRepository plays the role of collaborator
public Return returnMe(PurchaseRepository purchaseRepository, int quantity) {
return purchaseRepository.returnPurchase(this, quantity);
}
}
public class PurchaseRepositoryImpl implements PurchaseRepository {
// returnd Purchase object has its id set up
public Purchase getOldestPurchase() {
// logic in order to get oldest purchase
}
public Return returnPurchase(Purchase purchase, quantity) {
// logic in order to save a return record
// Some ORM frameworks returns ids when a record is saved. In my case, Hibernate or NHibernate (.NET) fulfill ths requirement
// Then purchaseId, returnId and quantity is saved in "returns applied" table
}
}
public class PurchaseServiceImpl implements PurchaseService {
// Injected through dependency injection
private PurchaseRepository purchaseRepository;
// Spring transaction boundary, for example
// Notice returnPurchase method returns a Return object
public Return returnPurchase(int quantity) {
Purchase purchase = purchaseRepository.getOldestPurchase();
return purchase.returnMe(purchaseRepository, quantity);
}
}